Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=29b9ccd261be025aaf75e…
Commit: 29b9ccd261be025aaf75e58e5d2547e818ef22c3
Parent: f96fd9961d1a80999a38b45c1d173e3df1de83e5
Author: Bryn M. Reeves <bmr(a)redhat.com>
AuthorDate: Thu Jun 28 14:25:30 2018 +0100
Committer: Bryn M. Reeves <bmr(a)redhat.com>
CommitterDate: Thu Jun 28 14:25:30 2018 +0100
dmsetup: fix error propagation in _display_info_cols()
Commit 3f35146 added a check on the value returned by the
_display_info_cols() function:
1024 if (!_switches[COLS_ARG])
1025 _display_info_long(dmt, &info);
1026 else
1027 r = _display_info_cols(dmt, &info);
1028
1029 return r;
This exposes a bug in the dmstats code in _display_info_cols:
the fact that a device has no regions is explicitly not an error
(and is documented as such in the code), but since the return
code is not changed before leaving the function it is now treated
as an error leading to:
# dmstats list
Command failed.
When no regions exist.
Set the return code to the correct value before returning.
---
libdm/dm-tools/dmsetup.c | 4 +++-
1 files changed, 3 insertions(+), 1 deletions(-)
diff --git a/libdm/dm-tools/dmsetup.c b/libdm/dm-tools/dmsetup.c
index 44386a6..fb1a574 100644
--- a/libdm/dm-tools/dmsetup.c
+++ b/libdm/dm-tools/dmsetup.c
@@ -943,8 +943,10 @@ static int _display_info_cols(struct dm_task *dmt, struct dm_info *info)
goto_out;
/* No regions to report is not an error */
- if (!dm_stats_get_nr_regions(obj.stats))
+ if (!dm_stats_get_nr_regions(obj.stats)) {
+ r = 1;
goto out;
+ }
}
/* group report with no groups? */
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=29b9ccd261be025aaf75e…
Commit: 29b9ccd261be025aaf75e58e5d2547e818ef22c3
Parent: f96fd9961d1a80999a38b45c1d173e3df1de83e5
Author: Bryn M. Reeves <bmr(a)redhat.com>
AuthorDate: Thu Jun 28 14:25:30 2018 +0100
Committer: Bryn M. Reeves <bmr(a)redhat.com>
CommitterDate: Thu Jun 28 14:25:30 2018 +0100
dmsetup: fix error propagation in _display_info_cols()
Commit 3f35146 added a check on the value returned by the
_display_info_cols() function:
1024 if (!_switches[COLS_ARG])
1025 _display_info_long(dmt, &info);
1026 else
1027 r = _display_info_cols(dmt, &info);
1028
1029 return r;
This exposes a bug in the dmstats code in _display_info_cols:
the fact that a device has no regions is explicitly not an error
(and is documented as such in the code), but since the return
code is not changed before leaving the function it is now treated
as an error leading to:
# dmstats list
Command failed.
When no regions exist.
Set the return code to the correct value before returning.
---
libdm/dm-tools/dmsetup.c | 4 +++-
1 files changed, 3 insertions(+), 1 deletions(-)
diff --git a/libdm/dm-tools/dmsetup.c b/libdm/dm-tools/dmsetup.c
index 44386a6..fb1a574 100644
--- a/libdm/dm-tools/dmsetup.c
+++ b/libdm/dm-tools/dmsetup.c
@@ -943,8 +943,10 @@ static int _display_info_cols(struct dm_task *dmt, struct dm_info *info)
goto_out;
/* No regions to report is not an error */
- if (!dm_stats_get_nr_regions(obj.stats))
+ if (!dm_stats_get_nr_regions(obj.stats)) {
+ r = 1;
goto out;
+ }
}
/* group report with no groups? */
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=6b3a4aac0954264c3f054…
Commit: 6b3a4aac0954264c3f054e7dd6c87babca77c244
Parent: 106ee05ba01146abff248a4e7eae77982fda9ebd
Author: Zdenek Kabelac <zkabelac(a)redhat.com>
AuthorDate: Fri Jun 22 18:45:48 2018 +0200
Committer: Zdenek Kabelac <zkabelac(a)redhat.com>
CommitterDate: Fri Jun 22 23:37:36 2018 +0200
vcfgrestore: add prompt with active volumes
Add check for active device with names matching restored VG.
When such devices are present in dm table, prompt user, if he
wish to continue.
---
WHATS_NEW | 1 +
tools/vgcfgrestore.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 76 insertions(+), 1 deletions(-)
diff --git a/WHATS_NEW b/WHATS_NEW
index 70fb76a..d90ff3a 100644
--- a/WHATS_NEW
+++ b/WHATS_NEW
@@ -1,5 +1,6 @@
Version 3.0.0
=============
+ Enhance vgcfgrestore to check for active LVs in restored VG.
Configure supports --disable-silent-rules for verbose builds.
Fix unmonitoring of merging snapshots.
Cache can uses metadata format 2 with cleaner policy.
diff --git a/tools/vgcfgrestore.c b/tools/vgcfgrestore.c
index 0b03687..0dd7d4d 100644
--- a/tools/vgcfgrestore.c
+++ b/tools/vgcfgrestore.c
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
- * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2004-2018 Red Hat, Inc. All rights reserved.
*
* This file is part of LVM2.
*
@@ -15,11 +15,70 @@
#include "tools.h"
#include "daemons/lvmetad/lvmetad-client.h"
+#include "device_mapper/all.h"
+#include "device_mapper/misc/dm-ioctl.h"
+
+/*
+ * Check if there are any active volumes from restored vg_name.
+ * We can prompt user, as such operation may make some serious
+ * troubles later, when user will try to continue such devices.
+ */
+static int _check_all_dm_devices(const char *vg_name, unsigned *found)
+{
+ struct dm_task *dmt;
+ struct dm_names *names;
+ char vgname_buf[DM_NAME_LEN * 2];
+ char *vgname, *lvname, *lvlayer;
+ unsigned next = 0;
+ int r = 1;
+
+ if (!(dmt = dm_task_create(DM_DEVICE_LIST)))
+ return_0;
+
+ if (!dm_task_run(dmt)) {
+ r = 0;
+ goto_out;
+ }
+
+ if (!(names = dm_task_get_names(dmt))) {
+ r = 0;
+ goto_out;
+ }
+
+ if (!names->dev) {
+ log_verbose("No devices found.");
+ goto out;
+ }
+
+ do {
+ /* TODO: Do we want to validate UUID LVM- prefix as well ? */
+ names = (struct dm_names *)((char *) names + next);
+ if (!dm_strncpy(vgname_buf, names->name, sizeof(vgname_buf))) {
+ r = 0;
+ goto_out;
+ }
+ vgname = vgname_buf;
+ if (!dm_split_lvm_name(NULL, NULL, &vgname, &lvname, &lvlayer)) {
+ r = 0;
+ goto_out;
+ }
+ if (strcmp(vgname, vg_name) == 0) {
+ log_print("Volume group %s has active volume: %s.", vgname, lvname);
+ (*found)++;
+ }
+ next = names->next;
+ } while (next);
+
+out:
+ dm_task_destroy(dmt);
+ return r;
+}
int vgcfgrestore(struct cmd_context *cmd, int argc, char **argv)
{
const char *vg_name = NULL;
int lvmetad_rescan = 0;
+ unsigned found = 0;
int ret;
if (argc == 1) {
@@ -47,6 +106,21 @@ int vgcfgrestore(struct cmd_context *cmd, int argc, char **argv)
return ECMD_PROCESSED;
}
+ if (!_check_all_dm_devices(vg_name, &found)) {
+ log_warn("WARNING: Failed to check for active volumes in volume group \"%s\".", vg_name);
+ } else if (found) {
+ log_warn("WARNING: Found %u active volume(s) in volume group \"%s\".",
+ found, vg_name);
+ log_print("Restoring VG with active LVs, may cause mismatch with its metadata.");
+ if (!arg_is_set(cmd, yes_ARG) &&
+ yes_no_prompt("Do you really want to proceed with restore of volume group \"%s\", "
+ "while %u volume(s) are active? [y/n]: ",
+ vg_name, found) == 'n') {
+ log_error("Restore aborted.");
+ return ECMD_FAILED;
+ }
+ }
+
/*
* lvmetad does not handle a VG being restored, which would require
* vg_remove of the existing VG, then vg_update of the restored VG. A
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=086f1ef4a0f59ee247067…
Commit: 086f1ef4a0f59ee247067e850b9b9ef0cdf5b1c3
Parent: 254e5c5d119447bcb8b160a4b4e5b0c36e9af5ba
Author: Zdenek Kabelac <zkabelac(a)redhat.com>
AuthorDate: Tue Jun 19 15:07:50 2018 +0200
Committer: Zdenek Kabelac <zkabelac(a)redhat.com>
CommitterDate: Fri Jun 22 15:36:34 2018 +0200
build: make generate
---
conf/example.conf.in | 33 +++++----------------------------
man/lvchange.8_pregen | 15 ---------------
man/lvdisplay.8_pregen | 11 -----------
man/lvm-fullreport.8_pregen | 11 -----------
man/lvs.8_pregen | 11 -----------
man/pvchange.8_pregen | 11 -----------
man/pvdisplay.8_pregen | 11 -----------
man/pvs.8_pregen | 11 -----------
man/vgchange.8_pregen | 31 -------------------------------
man/vgconvert.8_pregen | 9 +++++----
man/vgdisplay.8_pregen | 11 -----------
man/vgs.8_pregen | 11 -----------
12 files changed, 10 insertions(+), 166 deletions(-)
diff --git a/conf/example.conf.in b/conf/example.conf.in
index 97daab2..83f9f42 100644
--- a/conf/example.conf.in
+++ b/conf/example.conf.in
@@ -151,21 +151,15 @@ devices {
# global_filter = [ "a|.*/|" ]
# Configuration option devices/cache_dir.
- # Directory in which to store the device cache file.
- # The results of filtering are cached on disk to avoid rescanning dud
- # devices (which can take a very long time). By default this cache is
- # stored in a file named .cache. It is safe to delete this file; the
- # tools regenerate it. If obtain_device_list_from_udev is enabled, the
- # list of devices is obtained from udev and any existing .cache file
- # is removed.
+ # This setting is no longer used.
cache_dir = "@DEFAULT_SYS_DIR@/@DEFAULT_CACHE_SUBDIR@"
# Configuration option devices/cache_file_prefix.
- # A prefix used before the .cache file name. See devices/cache_dir.
+ # This setting is no longer used.
cache_file_prefix = ""
# Configuration option devices/write_cache_state.
- # Enable/disable writing the cache file. See devices/cache_dir.
+ # This setting is no longer used.
write_cache_state = 1
# Configuration option devices/types.
@@ -269,11 +263,7 @@ devices {
ignore_lvm_mirrors = 1
# Configuration option devices/disable_after_error_count.
- # Number of I/O errors after which a device is skipped.
- # During each LVM operation, errors received from each device are
- # counted. If the counter of a device exceeds the limit set here,
- # no further I/O is sent to that device for the remainder of the
- # operation. Setting this to 0 disables the counters altogether.
+ # This setting is no longer used.
disable_after_error_count = 0
# Configuration option devices/require_restorefile_with_uuid.
@@ -1557,20 +1547,7 @@ activation {
# stripesize = 64
# Configuration option metadata/dirs.
- # Directories holding live copies of text format metadata.
- # These directories must not be on logical volumes!
- # It's possible to use LVM with a couple of directories here,
- # preferably on different (non-LV) filesystems, and with no other
- # on-disk metadata (pvmetadatacopies = 0). Or this can be in addition
- # to on-disk metadata areas. The feature was originally added to
- # simplify testing and is not supported under low memory situations -
- # the machine could lock up. Never edit any files in these directories
- # by hand unless you are absolutely sure you know what you are doing!
- # Use the supplied toolset to make changes (e.g. vgcfgrestore).
- #
- # Example
- # dirs = [ "/etc/lvm/metadata", "/mnt/disk2/lvm/metadata2" ]
- #
+ # This setting is no longer used.
# This configuration option is advanced.
# This configuration option does not have a default value defined.
# }
diff --git a/man/lvchange.8_pregen b/man/lvchange.8_pregen
index 8f9783a..03e0b1c 100644
--- a/man/lvchange.8_pregen
+++ b/man/lvchange.8_pregen
@@ -97,10 +97,6 @@ lvchange - Change the attributes of logical volume(s)
.ad b
.br
.ad l
- \fB--ignoreskippedcluster\fP
-.ad b
-.br
-.ad l
\fB--lockopt\fP \fIString\fP
.ad b
.br
@@ -520,10 +516,6 @@ Common options for command:
.ad b
.br
.ad l
-[ \fB--ignoreskippedcluster\fP ]
-.ad b
-.br
-.ad l
[ \fB--noudevsync\fP ]
.ad b
.br
@@ -818,13 +810,6 @@ Do not use this if dmeventd is already monitoring a device.
.ad b
.HP
.ad l
-\fB--ignoreskippedcluster\fP
-.br
-Use to avoid exiting with an non-zero status code if the command is run
-without clustered locking and clustered VGs are skipped.
-.ad b
-.HP
-.ad l
\fB--lockopt\fP \fIString\fP
.br
Used to pass options for special cases to lvmlockd.
diff --git a/man/lvdisplay.8_pregen b/man/lvdisplay.8_pregen
index 6211a47..9a8962b 100644
--- a/man/lvdisplay.8_pregen
+++ b/man/lvdisplay.8_pregen
@@ -72,10 +72,6 @@ and more, using a more compact and configurable output format.
.ad b
.br
.ad l
-[ \fB--ignoreskippedcluster\fP ]
-.ad b
-.br
-.ad l
[ \fB--logonly\fP ]
.ad b
.br
@@ -284,13 +280,6 @@ operations after locking failures.
.ad b
.HP
.ad l
-\fB--ignoreskippedcluster\fP
-.br
-Use to avoid exiting with an non-zero status code if the command is run
-without clustered locking and clustered VGs are skipped.
-.ad b
-.HP
-.ad l
\fB--lockopt\fP \fIString\fP
.br
Used to pass options for special cases to lvmlockd.
diff --git a/man/lvm-fullreport.8_pregen b/man/lvm-fullreport.8_pregen
index 4a8d7b3..111b8ef 100644
--- a/man/lvm-fullreport.8_pregen
+++ b/man/lvm-fullreport.8_pregen
@@ -56,10 +56,6 @@ if information changes between commands.
.ad b
.br
.ad l
-[ \fB--ignoreskippedcluster\fP ]
-.ad b
-.br
-.ad l
[ \fB--logonly\fP ]
.ad b
.br
@@ -255,13 +251,6 @@ operations after locking failures.
.ad b
.HP
.ad l
-\fB--ignoreskippedcluster\fP
-.br
-Use to avoid exiting with an non-zero status code if the command is run
-without clustered locking and clustered VGs are skipped.
-.ad b
-.HP
-.ad l
\fB--lockopt\fP \fIString\fP
.br
Used to pass options for special cases to lvmlockd.
diff --git a/man/lvs.8_pregen b/man/lvs.8_pregen
index b5128f9..fecca99 100644
--- a/man/lvs.8_pregen
+++ b/man/lvs.8_pregen
@@ -60,10 +60,6 @@ lvs produces formatted output about LVs.
.ad b
.br
.ad l
-[ \fB--ignoreskippedcluster\fP ]
-.ad b
-.br
-.ad l
[ \fB--logonly\fP ]
.ad b
.br
@@ -270,13 +266,6 @@ operations after locking failures.
.ad b
.HP
.ad l
-\fB--ignoreskippedcluster\fP
-.br
-Use to avoid exiting with an non-zero status code if the command is run
-without clustered locking and clustered VGs are skipped.
-.ad b
-.HP
-.ad l
\fB--lockopt\fP \fIString\fP
.br
Used to pass options for special cases to lvmlockd.
diff --git a/man/pvchange.8_pregen b/man/pvchange.8_pregen
index 5da8eca..8c9288d 100644
--- a/man/pvchange.8_pregen
+++ b/man/pvchange.8_pregen
@@ -98,10 +98,6 @@ Common options for command:
.ad b
.br
.ad l
-[ \fB--ignoreskippedcluster\fP ]
-.ad b
-.br
-.ad l
[ \fB--reportformat\fP \fBbasic\fP|\fBjson\fP ]
.ad b
.RE
@@ -244,13 +240,6 @@ Display help text.
.ad b
.HP
.ad l
-\fB--ignoreskippedcluster\fP
-.br
-Use to avoid exiting with an non-zero status code if the command is run
-without clustered locking and clustered VGs are skipped.
-.ad b
-.HP
-.ad l
\fB--lockopt\fP \fIString\fP
.br
Used to pass options for special cases to lvmlockd.
diff --git a/man/pvdisplay.8_pregen b/man/pvdisplay.8_pregen
index 5f39e7b..9dbcac3 100644
--- a/man/pvdisplay.8_pregen
+++ b/man/pvdisplay.8_pregen
@@ -72,10 +72,6 @@ and more, using a more compact and configurable output format.
.ad b
.br
.ad l
-[ \fB--ignoreskippedcluster\fP ]
-.ad b
-.br
-.ad l
[ \fB--logonly\fP ]
.ad b
.br
@@ -271,13 +267,6 @@ operations after locking failures.
.ad b
.HP
.ad l
-\fB--ignoreskippedcluster\fP
-.br
-Use to avoid exiting with an non-zero status code if the command is run
-without clustered locking and clustered VGs are skipped.
-.ad b
-.HP
-.ad l
\fB--lockopt\fP \fIString\fP
.br
Used to pass options for special cases to lvmlockd.
diff --git a/man/pvs.8_pregen b/man/pvs.8_pregen
index 59f1e3d..53c259c 100644
--- a/man/pvs.8_pregen
+++ b/man/pvs.8_pregen
@@ -56,10 +56,6 @@ pvs produces formatted output about PVs.
.ad b
.br
.ad l
-[ \fB--ignoreskippedcluster\fP ]
-.ad b
-.br
-.ad l
[ \fB--logonly\fP ]
.ad b
.br
@@ -257,13 +253,6 @@ operations after locking failures.
.ad b
.HP
.ad l
-\fB--ignoreskippedcluster\fP
-.br
-Use to avoid exiting with an non-zero status code if the command is run
-without clustered locking and clustered VGs are skipped.
-.ad b
-.HP
-.ad l
\fB--lockopt\fP \fIString\fP
.br
Used to pass options for special cases to lvmlockd.
diff --git a/man/vgchange.8_pregen b/man/vgchange.8_pregen
index a9ac546..94dbd88 100644
--- a/man/vgchange.8_pregen
+++ b/man/vgchange.8_pregen
@@ -75,10 +75,6 @@ vgchange - Change volume group attributes
.ad b
.br
.ad l
- \fB--ignoreskippedcluster\fP
-.ad b
-.br
-.ad l
\fB--lockopt\fP \fIString\fP
.ad b
.br
@@ -279,10 +275,6 @@ required, after which the others are optional.
.ad b
.br
.ad l
-[ \fB--ignoreskippedcluster\fP ]
-.ad b
-.br
-.ad l
[ \fB--noudevsync\fP ]
.ad b
.br
@@ -333,10 +325,6 @@ Start or stop monitoring LVs from dmeventd.
.ad b
.br
.ad l
-[ \fB--ignoreskippedcluster\fP ]
-.ad b
-.br
-.ad l
[ \fB--noudevsync\fP ]
.ad b
.br
@@ -379,10 +367,6 @@ Start or stop processing LV conversions.
.ad b
.br
.ad l
-[ \fB--ignoreskippedcluster\fP ]
-.ad b
-.br
-.ad l
[ \fB--noudevsync\fP ]
.ad b
.br
@@ -449,10 +433,6 @@ Activate or deactivate LVs.
.ad b
.br
.ad l
-[ \fB--ignoreskippedcluster\fP ]
-.ad b
-.br
-.ad l
[ \fB--noudevsync\fP ]
.ad b
.br
@@ -503,10 +483,6 @@ Reactivate LVs using the latest metadata.
.ad b
.br
.ad l
-[ \fB--ignoreskippedcluster\fP ]
-.ad b
-.br
-.ad l
[ \fB--noudevsync\fP ]
.ad b
.br
@@ -800,13 +776,6 @@ Do not use this if dmeventd is already monitoring a device.
.ad b
.HP
.ad l
-\fB--ignoreskippedcluster\fP
-.br
-Use to avoid exiting with an non-zero status code if the command is run
-without clustered locking and clustered VGs are skipped.
-.ad b
-.HP
-.ad l
\fB--lockopt\fP \fIString\fP
.br
Used to pass options for special cases to lvmlockd.
diff --git a/man/vgconvert.8_pregen b/man/vgconvert.8_pregen
index 16e1fc8..b2a6027 100644
--- a/man/vgconvert.8_pregen
+++ b/man/vgconvert.8_pregen
@@ -8,9 +8,9 @@ vgconvert - Change volume group metadata format
[ \fIoption_args\fP ]
.br
.SH DESCRIPTION
-vgconvert converts VG metadata from one format to another. This command
-is no longer used because this version of lvm no longer supports the LVM1
-format.
+vgconvert is no longer a part of LVM. It was removed along with
+support for the LVM1 format. Use an older version of LVM to
+convert VGs from the LVM1 format to LVM2.
.SH USAGE
\fBvgconvert\fP \fIVG\fP ...
.br
@@ -46,7 +46,6 @@ format.
[ COMMON_OPTIONS ]
.RE
.br
-
Common options for lvm:
.
.RS 4
@@ -106,6 +105,7 @@ Common options for lvm:
[ \fB--version\fP ]
.ad b
.RE
+
.SH OPTIONS
.HP
.ad l
@@ -301,6 +301,7 @@ capital letters mean multiple of 1000.)
.SH ENVIRONMENT VARIABLES
See \fBlvm\fP(8) for information about environment variables used by lvm.
For example, LVM_VG_NAME can generally be substituted for a required VG parameter.
+
.SH SEE ALSO
.BR lvm (8)
diff --git a/man/vgdisplay.8_pregen b/man/vgdisplay.8_pregen
index 89e3603..7c8fb12 100644
--- a/man/vgdisplay.8_pregen
+++ b/man/vgdisplay.8_pregen
@@ -67,10 +67,6 @@ and more, using a more compact and configurable output format.
.ad b
.br
.ad l
-[ \fB--ignoreskippedcluster\fP ]
-.ad b
-.br
-.ad l
[ \fB--logonly\fP ]
.ad b
.br
@@ -266,13 +262,6 @@ operations after locking failures.
.ad b
.HP
.ad l
-\fB--ignoreskippedcluster\fP
-.br
-Use to avoid exiting with an non-zero status code if the command is run
-without clustered locking and clustered VGs are skipped.
-.ad b
-.HP
-.ad l
\fB--lockopt\fP \fIString\fP
.br
Used to pass options for special cases to lvmlockd.
diff --git a/man/vgs.8_pregen b/man/vgs.8_pregen
index 681711b..c8a9611 100644
--- a/man/vgs.8_pregen
+++ b/man/vgs.8_pregen
@@ -52,10 +52,6 @@ vgs produces formatted output about VGs.
.ad b
.br
.ad l
-[ \fB--ignoreskippedcluster\fP ]
-.ad b
-.br
-.ad l
[ \fB--logonly\fP ]
.ad b
.br
@@ -252,13 +248,6 @@ operations after locking failures.
.ad b
.HP
.ad l
-\fB--ignoreskippedcluster\fP
-.br
-Use to avoid exiting with an non-zero status code if the command is run
-without clustered locking and clustered VGs are skipped.
-.ad b
-.HP
-.ad l
\fB--lockopt\fP \fIString\fP
.br
Used to pass options for special cases to lvmlockd.
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=42f7caf1c267a5b75ee38…
Commit: 42f7caf1c267a5b75ee38ea77a7e2fd7c582e704
Parent: f85a010a6bc9dc0414ccdf9c6b195c78231f1914
Author: David Teigland <teigland(a)redhat.com>
AuthorDate: Wed Jun 20 11:32:45 2018 -0500
Committer: David Teigland <teigland(a)redhat.com>
CommitterDate: Wed Jun 20 14:08:12 2018 -0500
scan: work around udev problems by avoiding open RDWR
udev creates a train wreck of events if we open devices
with RDWR. Until we can fix/disable/scrap udev, work around
this by opening RDONLY and then closing/reopening RDWR when
a write is needed. This invalidates the bcache blocks for
the device before writing so it can trigger unnecessary
rereading.
---
lib/device/device.h | 1 +
lib/label/label.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 55 insertions(+), 2 deletions(-)
diff --git a/lib/device/device.h b/lib/device/device.h
index 00e398a..e879dbb 100644
--- a/lib/device/device.h
+++ b/lib/device/device.h
@@ -35,6 +35,7 @@
#define DEV_BCACHE_EXCL 0x00001000 /* bcache_fd should be open EXCL */
#define DEV_FILTER_AFTER_SCAN 0x00002000 /* apply filter after bcache has data */
#define DEV_FILTER_OUT_SCAN 0x00004000 /* filtered out during label scan */
+#define DEV_BCACHE_WRITE 0x00008000 /* bcache_fd is open with RDWR */
/*
* Support for external device info.
diff --git a/lib/label/label.c b/lib/label/label.c
index 065c01f..d2c8685 100644
--- a/lib/label/label.c
+++ b/lib/label/label.c
@@ -467,12 +467,24 @@ static int _scan_dev_open(struct device *dev)
name_sl = dm_list_item(name_list, struct dm_str_list);
name = name_sl->str;
- flags |= O_RDWR;
flags |= O_DIRECT;
flags |= O_NOATIME;
- if (dev->flags & DEV_BCACHE_EXCL)
+ /*
+ * FIXME: udev is a train wreck when we open RDWR and close, so we
+ * need to only use RDWR when we actually need to write, and use
+ * RDONLY otherwise. Fix, disable or scrap udev nonsense so we can
+ * just open with RDWR by default.
+ */
+
+ if (dev->flags & DEV_BCACHE_EXCL) {
flags |= O_EXCL;
+ flags |= O_RDWR;
+ } else if (dev->flags & DEV_BCACHE_WRITE) {
+ flags |= O_RDWR;
+ } else {
+ flags |= O_RDONLY;
+ }
retry_open:
@@ -1124,7 +1136,14 @@ int label_scan_open(struct device *dev)
int label_scan_open_excl(struct device *dev)
{
+ if (_in_bcache(dev) && !(dev->flags & DEV_BCACHE_EXCL)) {
+ /* FIXME: avoid tossing out bcache blocks just to replace fd. */
+ log_debug("Close and reopen excl %s", dev_name(dev));
+ bcache_invalidate_fd(scan_bcache, dev->bcache_fd);
+ _scan_dev_close(dev);
+ }
dev->flags |= DEV_BCACHE_EXCL;
+ dev->flags |= DEV_BCACHE_WRITE;
return label_scan_open(dev);
}
@@ -1166,8 +1185,19 @@ bool dev_write_bytes(struct device *dev, uint64_t start, size_t len, void *data)
return false;
}
+ if (!(dev->flags & DEV_BCACHE_WRITE)) {
+ /* FIXME: avoid tossing out bcache blocks just to replace fd. */
+ log_debug("Close and reopen to write %s", dev_name(dev));
+ bcache_invalidate_fd(scan_bcache, dev->bcache_fd);
+ _scan_dev_close(dev);
+
+ dev->flags |= DEV_BCACHE_WRITE;
+ label_scan_open(dev);
+ }
+
if (dev->bcache_fd <= 0) {
/* This is not often needed, perhaps only with lvmetad. */
+ dev->flags |= DEV_BCACHE_WRITE;
if (!label_scan_open(dev)) {
log_error("Error opening device %s for writing at %llu length %u.",
dev_name(dev), (unsigned long long)start, (uint32_t)len);
@@ -1201,8 +1231,19 @@ bool dev_write_zeros(struct device *dev, uint64_t start, size_t len)
return false;
}
+ if (!(dev->flags & DEV_BCACHE_WRITE)) {
+ /* FIXME: avoid tossing out bcache blocks just to replace fd. */
+ log_debug("Close and reopen to write %s", dev_name(dev));
+ bcache_invalidate_fd(scan_bcache, dev->bcache_fd);
+ _scan_dev_close(dev);
+
+ dev->flags |= DEV_BCACHE_WRITE;
+ label_scan_open(dev);
+ }
+
if (dev->bcache_fd <= 0) {
/* This is not often needed, perhaps only with lvmetad. */
+ dev->flags |= DEV_BCACHE_WRITE;
if (!label_scan_open(dev)) {
log_error("Error opening device %s for writing at %llu length %u.",
dev_name(dev), (unsigned long long)start, (uint32_t)len);
@@ -1236,8 +1277,19 @@ bool dev_set_bytes(struct device *dev, uint64_t start, size_t len, uint8_t val)
return false;
}
+ if (!(dev->flags & DEV_BCACHE_WRITE)) {
+ /* FIXME: avoid tossing out bcache blocks just to replace fd. */
+ log_debug("Close and reopen to write %s", dev_name(dev));
+ bcache_invalidate_fd(scan_bcache, dev->bcache_fd);
+ _scan_dev_close(dev);
+
+ dev->flags |= DEV_BCACHE_WRITE;
+ label_scan_open(dev);
+ }
+
if (dev->bcache_fd <= 0) {
/* This is not often needed, perhaps only with lvmetad. */
+ dev->flags |= DEV_BCACHE_WRITE;
if (!label_scan_open(dev)) {
log_error("Error opening device %s for writing at %llu length %u.",
dev_name(dev), (unsigned long long)start, (uint32_t)len);
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=f426de669957e39d62ad2…
Commit: f426de669957e39d62ad26748b9e6704571f7456
Parent: 0000000000000000000000000000000000000000
Author: Marian Csontos <mcsontos(a)redhat.com>
AuthorDate: 2018-06-18 05:22 +0000
Committer: Marian Csontos <mcsontos(a)redhat.com>
CommitterDate: 2018-06-18 05:22 +0000
annotated tag: v2_02_179 has been created
at f426de669957e39d62ad26748b9e6704571f7456 (tag)
tagging 6283f5ea3f33fa022ec1f25baba630cbc5fdd1c5 (commit)
replaces v2_02_178
Release 2.02.179
Bug fix release, fixing mostly problems with cache a lvmlockd.
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.22 (GNU/Linux)
iQIcBAABAgAGBQJbJ0H2AAoJELkRJDHlCQOfCrQP/iWdSFUtDn2qnL5k6PMjV+co
iVVd0PAkMkbra6Xesxhz6tePAV6WyYdswQmdgGKhnko/BtjuO88kFjEriLqS0sux
YRakjqoXKOW7RZgTeEowYeGQX1Rxl0Oos/xG/KJRVS0O3/QAojQsOeQ6VSdpvlKV
10g/G2/pdIxa9bwMOlQMKQAJnoS8bRJ4+4W8c5wFNJi2OZLcPyoSQYwiLDpeUhYo
xqJlgnzvkqEfaMg/INpdWgqvpC0slXcV2I7fNvqZdEQTgFdSLjncGRB3lWR4Pl4A
f0/rLGgtOpCW48zNgUxpdx1TYOEf45OOXINfy8M/R8D1yoa1f3r1b6IRfrM+kXN4
Kv7q2TDfleO92nZ3xPyPVx3bLsX555sFLqK6OnKxoKHcHTLM+A6Qtdvd2I/xmZQv
TmUCmp+CGsmMx/IwWF9Buoq67hXeGn9YDiOK1bkVcKOr8p5Gp++2pN2yN8f1Kb83
Qi7HGWQf/P+Ya6+qf/BqFnWxy7BNstUXCRJ7cZdmiMiSB5oWNxrABopID/QfIcXo
B8tPm49P3N7U3dBrzl0Dz4KiK9B3MKOBx2AtvSkctXYOPrbOMOe20oOfBPj6XnD9
OaHqKvB8u+gVgonFf/oiuoEsQD95EYYZc3IFYOGJ0fb5dCTje4CkVVSujUyopU00
I5X+Y5te3LGivWIVZfNn
=M1dD
-----END PGP SIGNATURE-----
Alasdair G Kergon (1):
format_text: Use versionsort to sort archive files
David Teigland (42):
scan: refresh paths and retry open
scan: removed failed paths for devices
tests: fix skipping logic for lvmpolld and lvmlockd
tests: some missed skip with lvmlockd
lvmlockd: accept repeated global lock requests
tests: separate lvmlockd tests with or without lvmetad
lvmlockd: do not use an LV lock for some lvchange options
config: revert to normal locking when no cluster
lvmlockd: enable mirror split and merge with dlm lock_type
lvmlockd: enable lvcreate of thin pool and thin lv in one command
lvmlockd: enable creation of cache pool with lvcreate
tests: enable lvmlockd for passing tests
tests: vgcreate-usage update for lvmlockd
tests: vgchange-usage update for lvmlockd
lvmlockd: fix vgimportclone of a shared VG
pvremove: skip lvmlockd locks for forced clearing
tests: process-each-duplicate-pvs update for lvmlockd
tests: add missing file
lvmlockd: enable repairing shared VG while reading it
tests: enable vg repair tests with lvmlockd
tests: enable pvmove-restart with lvmlockd
lvmlockd: enable lvcreate of new LV plus existing cache pool
tests: enable lvcreate cache tests with lvmlockd
tests: change lvcreate syntax to allow lvmlockd
vgs: add report field for shared
lvmlockd: enable lvcreate -H -L LV
tests: enable lvmlockd with tests using lvcreate -H -L LV
tests: enable metadata-balance with lvmlockd
lvmlockd: fix reverting new lv in error path
tests: enable lvcreate-pvtags with lvmlockd
lvmlockd: improve error message for existing lockspace
tests: enable more working tests with lvmlockd
tests: enable non-working tests with lvmlockd
man lvmlockd: update list of limitations
devices: clean up io error messages
lvmlockd: fix missing lock_type null check
man lvmlockd: remove unnecessary reference to lvmetad
lvmlockd: skip repair lock upgrade for non shared vgs
WHATS_NEW for recent changes
lvmlockd: update method for changing clustered VG
man: updates to lvmlockd
man: update lvmsystemid wording
Joe Thornber (8):
radix_tree: add remove method
radix_tree_t: knock out some debug
data-struct/radix-tree: pass the value dtr into create.
Merge branch '2018-05-29-radix-tree-iterate' into 2018-05-23-radix-tree-remove
radix-tree: call the value dtr when removing an entry.
radix-tree: radix_tree_remove_prefix()
radix-tree: radix_tree_iterate()
radix-tree: fix some bugs in remove_prefix and iterate
Marian Csontos (5):
post-release
Merge remote-tracking branch 'origin/master'
post-release
Merge branch 'dev-mcsontos-release' into stable
pre-release
Zdenek Kabelac (2):
man: another missed typo for thin plugin
tests: improve usability on older systems
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=3fd75d1bcd714b02fb2b8…
Commit: 3fd75d1bcd714b02fb2b843d1928b2a875402f37
Parent: 8eab37593eccbbb8c6d03a9bae2f6852c17a00a5
Author: David Teigland <teigland(a)redhat.com>
AuthorDate: Fri Jun 15 11:42:10 2018 -0500
Committer: David Teigland <teigland(a)redhat.com>
CommitterDate: Fri Jun 15 12:21:25 2018 -0500
scan: use full md filter when md 1.0 devices are present
The md filter can operate in two native modes:
- normal: reads only the start of each device
- full: reads both the start and end of each device
md 1.0 devices place the superblock at the end of the device,
so components of this version will only be identified and
excluded when lvm uses the full md filter.
Previously, the full md filter was only used in commands
that could write to the device. Now, the full md filter
is also applied when there is an md 1.0 device present
on the system. This means the 'pvs' command can avoid
displaying md 1.0 components (at the cost of doubling
the i/o to every device on the system.)
(The md filter can operate in a third mode, using udev,
but this is disabled by default because there have been
problems with reliability of the info returned from udev.)
---
lib/cache/lvmcache.c | 2 +-
lib/device/dev-md.c | 27 +++++++++---
lib/device/dev-type.h | 1 +
lib/filters/filter-md.c | 81 ++++++++++++++++-------------------
lib/label/label.c | 14 ++++++
test/shell/pvcreate-md-fake-hdr.sh | 3 +-
6 files changed, 75 insertions(+), 53 deletions(-)
diff --git a/lib/cache/lvmcache.c b/lib/cache/lvmcache.c
index c7e3903..f15b352 100644
--- a/lib/cache/lvmcache.c
+++ b/lib/cache/lvmcache.c
@@ -452,7 +452,7 @@ int lvmcache_dev_is_unchosen_duplicate(struct device *dev)
* unused_duplicate_devs list, and restrict what we allow done with it.
*
* In the case of md components, we usually filter these out in filter-md,
- * but in the special case of md superblocks <= 1.0 where the superblock
+ * but in the special case of md superblock version 1.0 where the superblock
* is at the end of the device, filter-md doesn't always eliminate them
* first, so we eliminate them here.
*
diff --git a/lib/device/dev-md.c b/lib/device/dev-md.c
index 8284c4d..ac99f42 100644
--- a/lib/device/dev-md.c
+++ b/lib/device/dev-md.c
@@ -142,13 +142,6 @@ static int _native_dev_is_md(struct device *dev, uint64_t *offset_found, int ful
* command if it should do a full check (cmd->use_full_md_check),
* and set it for commands that could possibly write to an md dev
* (pvcreate/vgcreate/vgextend).
- *
- * For old md versions with magic numbers at the end of devices,
- * the md dev components won't be filtered out here when full is 0,
- * so they will be scanned, and appear as duplicate PVs in lvmcache.
- * The md device itself will be chosen as the primary duplicate,
- * and the components are dropped from the list of duplicates in,
- * i.e. a kind of post-scan filtering.
*/
if (!full) {
sb_offset = 0;
@@ -414,6 +407,26 @@ unsigned long dev_md_stripe_width(struct dev_types *dt, struct device *dev)
return stripe_width_sectors;
}
+int dev_is_md_with_end_superblock(struct dev_types *dt, struct device *dev)
+{
+ char version_string[MD_MAX_SYSFS_SIZE];
+ const char *attribute = "metadata_version";
+
+ if (MAJOR(dev->dev) != dt->md_major)
+ return 0;
+
+ if (_md_sysfs_attribute_scanf(dt, dev, attribute,
+ "%s", &version_string) != 1)
+ return -1;
+
+ log_very_verbose("Device %s %s is %s.",
+ dev_name(dev), attribute, version_string);
+
+ if (!strcmp(version_string, "1.0"))
+ return 1;
+ return 0;
+}
+
#else
int dev_is_md(struct device *dev __attribute__((unused)),
diff --git a/lib/device/dev-type.h b/lib/device/dev-type.h
index 210a316..0e418d6 100644
--- a/lib/device/dev-type.h
+++ b/lib/device/dev-type.h
@@ -76,6 +76,7 @@ int wipe_known_signatures(struct cmd_context *cmd, struct device *dev, const cha
/* Type-specific device properties */
unsigned long dev_md_stripe_width(struct dev_types *dt, struct device *dev);
+int dev_is_md_with_end_superblock(struct dev_types *dt, struct device *dev);
/* Partitioning */
int major_max_partitions(struct dev_types *dt, int major);
diff --git a/lib/filters/filter-md.c b/lib/filters/filter-md.c
index 95e0aad..4c3f23d 100644
--- a/lib/filters/filter-md.c
+++ b/lib/filters/filter-md.c
@@ -30,50 +30,43 @@
*
* (This is assuming lvm.conf md_component_detection=1.)
*
- * If lvm does *not* ignore the components, then lvm will read lvm
- * labels from the md dev and from the component devs, and will see
- * them all as duplicates of each other. LVM duplicate resolution
- * will then kick in and keep the md dev around to use and ignore
- * the components.
- *
- * It is better to exclude the components as early as possible during
- * lvm processing, ideally before lvm even looks for labels on the
- * components, so that duplicate resolution can be avoided. There are
- * a number of ways that md components can be excluded earlier than
- * the duplicate resolution phase:
- *
- * - When external_device_info_source="udev", lvm discovers a device is
- * an md component by asking udev during the initial filtering phase.
- * However, lvm's default is to not use udev for this. The
- * alternative is "native" detection in which lvm tries to detect
- * md components itself.
- *
- * - When using native detection, lvm's md filter looks for the md
- * superblock at the start of devices. It will see the md superblock
- * on the components, exclude them in the md filter, and avoid
- * handling them later in duplicate resolution.
- *
- * - When using native detection, lvm's md filter will not detect
- * components when the md device has an older superblock version that
- * places the superblock at the end of the device. This case will
- * fall back to duplicate resolution to exclude components.
- *
- * A variation of the description above occurs for lvm commands that
- * intend to create new PVs on devices (pvcreate, vgcreate, vgextend).
- * For these commands, the native md filter also reads the end of all
- * devices to check for the odd md superblocks.
- *
- * (The reason that external_device_info_source is not set to udev by
- * default is that there have be issues with udev not being promptly
- * or reliably updated about md state changes, causing the udev info
- * that lvm uses to be occasionally wrong.)
- */
-
-/*
- * FIXME: for commands that want a full md check (pvcreate, vgcreate,
- * vgextend), we do an extra read at the end of every device that the
- * filter looks at. This isn't necessary; we only need to do the full
- * md check on the PVs that these commands are trying to use.
+ * If lvm does *not* ignore the components, then lvm may read lvm
+ * labels from the component devs and potentially the md dev,
+ * which can trigger duplicate detection, and/or cause lvm to display
+ * md components as PVs rather than ignoring them.
+ *
+ * If scanning md componenents causes duplicates to be seen, then
+ * the lvm duplicate resolution will exclude the components.
+ *
+ * The lvm md filter has three modes:
+ *
+ * 1. look for md superblock at the start of the device
+ * 2. look for md superblock at the start and end of the device
+ * 3. use udev to detect components
+ *
+ * mode 1 will not detect and exclude components of md devices
+ * that use superblock version 1.0 which is at the end of the device.
+ *
+ * mode 2 will detect these, but mode 2 doubles the i/o done by label
+ * scan, since there's a read at both the start and end of every device.
+ *
+ * mode 3 is used when external_device_info_source="udev". It does
+ * not require any io from lvm, but this mode is not used by default
+ * because there have been problems getting reliable info from udev.
+ *
+ * lvm uses mode 2 when:
+ *
+ * - the command is pvcreate/vgcreate/vgextend, which format new
+ * devices, and if the user ran these commands on a component
+ * device of an md device 1.0, then it would cause problems.
+ * FIXME: this would only really need to scan the end of the
+ * devices being formatted, not all devices.
+ *
+ * - it sees an md device on the system using version 1.0.
+ * The point of this is just to avoid displaying md components
+ * from the 'pvs' command.
+ * FIXME: the cost (double i/o) may not be worth the benefit
+ * (not showing md components).
*/
/*
diff --git a/lib/label/label.c b/lib/label/label.c
index de5aa88..065c01f 100644
--- a/lib/label/label.c
+++ b/lib/label/label.c
@@ -847,6 +847,20 @@ int label_scan(struct cmd_context *cmd)
bcache_invalidate_fd(scan_bcache, dev->bcache_fd);
_scan_dev_close(dev);
}
+
+ /*
+ * When md devices exist that use the old superblock at the
+ * end of the device, then in order to detect and filter out
+ * the component devices of those md devs, we need to enable
+ * the full md filter which scans both the start and the end
+ * of every device. This doubles the amount of scanning i/o,
+ * which we want to avoid. FIXME: it may not be worth the
+ * cost of double i/o just to avoid displaying md component
+ * devs in 'pvs', which is a pretty harmless effect from a
+ * pretty uncommon situation.
+ */
+ if (dev_is_md_with_end_superblock(cmd->dev_types, dev))
+ cmd->use_full_md_check = 1;
};
dev_iter_destroy(iter);
diff --git a/test/shell/pvcreate-md-fake-hdr.sh b/test/shell/pvcreate-md-fake-hdr.sh
index 13d810c..82dd8d4 100644
--- a/test/shell/pvcreate-md-fake-hdr.sh
+++ b/test/shell/pvcreate-md-fake-hdr.sh
@@ -95,6 +95,7 @@ sleep 1
# (when mdadm supports repair)
if mdadm --action=repair "$mddev" ; then
sleep 1
+ pvscan -vvvv
# should be showing correctly PV3 & PV4
- pvs
+ pvs -vvvv "$dev3" "$dev4"
fi
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=218c57410c32bfa7bf704…
Commit: 218c57410c32bfa7bf7044cc6d94fd9253d6b547
Parent: 752c39d91db55ae637fef3b8066e1a78f894609d
Author: Zdenek Kabelac <zkabelac(a)redhat.com>
AuthorDate: Thu Jun 14 21:07:59 2018 +0200
Committer: Zdenek Kabelac <zkabelac(a)redhat.com>
CommitterDate: Thu Jun 14 22:02:01 2018 +0200
pvscan: move start of polling into vgchange
Restoring polling for activated volumes lost with my recent commit:
75fed05d3ef648583764ff56cc577e0f3eba1bba and move start of polling
directly into _activate_lvs_in_vg() - as there we know exactly
if there was some volume even activated.
Also make it sharing same code for pvscan -aay.
---
tools/pvscan.c | 10 ----------
tools/vgchange.c | 25 +++++++++++++++++++------
2 files changed, 19 insertions(+), 16 deletions(-)
diff --git a/tools/pvscan.c b/tools/pvscan.c
index 508f283..61530ee 100644
--- a/tools/pvscan.c
+++ b/tools/pvscan.c
@@ -248,16 +248,6 @@ static int _pvscan_autoactivate_single(struct cmd_context *cmd, const char *vg_n
goto out;
}
- /*
- * After sucessfull activation we need to initialise polling
- * for all activated LVs in a VG. Possible enhancement would
- * be adding --poll y|n cmdline option for pvscan and call
- * init_background_polling routine in autoactivation handler.
- */
- log_debug_activation("Starting background polling for VG %s.", vg_name);
-
- if (!(vgchange_background_polling(cmd, vg)))
- goto_out;
out:
return ECMD_PROCESSED;
}
diff --git a/tools/vgchange.c b/tools/vgchange.c
index 55bc476..c513470 100644
--- a/tools/vgchange.c
+++ b/tools/vgchange.c
@@ -127,17 +127,29 @@ static int _activate_lvs_in_vg(struct cmd_context *cmd, struct volume_group *vg,
sigint_restore();
+ if (expected_count)
+ log_verbose("%sctivated %d logical volumes in volume group %s.",
+ is_change_activating(activate) ? "A" : "Dea",
+ count, vg->name);
+
+ /*
+ * After sucessfull activation we need to initialise polling
+ * for all activated LVs in a VG. Possible enhancement would
+ * be adding --poll y|n cmdline option for pvscan and call
+ * init_background_polling routine in autoactivation handler.
+ */
+ if (count && is_change_activating(activate) &&
+ !vgchange_background_polling(cmd, vg)) {
+ stack;
+ r = 0;
+ }
+
/* Wait until devices are available */
if (!sync_local_dev_names(vg->cmd)) {
log_error("Failed to sync local devices for VG %s.", vg->name);
r = 0;
}
- if (expected_count)
- log_verbose("%s %d logical volumes in volume group %s",
- is_change_activating(activate) ?
- "Activated" : "Deactivated", count, vg->name);
-
return r;
}
@@ -163,7 +175,8 @@ int vgchange_background_polling(struct cmd_context *cmd, struct volume_group *vg
int polled;
if (background_polling()) {
- polled = _poll_lvs_in_vg(cmd, vg);
+ log_debug_activation("Starting background polling for volume group \"%s\".", vg->name);
+ polled = _poll_lvs_in_vg(cmd, vg);
if (polled)
log_print_unless_silent("Background polling started for %d logical volume(s) "
"in volume group \"%s\"",
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=5b515db71ba13d2a4f328…
Commit: 5b515db71ba13d2a4f328be6a97bb309401c5c69
Parent: 52ab3c1584cd40572e82d6add7a7c71204fa9721
Author: Zdenek Kabelac <zkabelac(a)redhat.com>
AuthorDate: Thu Jun 14 20:57:02 2018 +0200
Committer: Zdenek Kabelac <zkabelac(a)redhat.com>
CommitterDate: Thu Jun 14 22:02:01 2018 +0200
build: better srcdir builddir support
With the move to top-level makefile - there are some issues
with subdir recursive makefile.
Make the building more tolerant for now until fully resolved.
---
Makefile.in | 3 ++-
test/Makefile.in | 2 +-
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/Makefile.in b/Makefile.in
index ae05aea..d076088 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -193,7 +193,8 @@ endif
endif
-include test/unit/Makefile
+# FIXME: Drop once top-level make is resolved
+-include test/unit/Makefile
include $(top_srcdir)/device_mapper/Makefile
include $(top_srcdir)/base/Makefile
diff --git a/test/Makefile.in b/test/Makefile.in
index 4c3c715..ca2e2e1 100644
--- a/test/Makefile.in
+++ b/test/Makefile.in
@@ -357,7 +357,7 @@ LIB = $(addprefix lib/, $(LIB_SHARED) $(LIB_LOCAL) $(LIB_NOT) $(LIB_LINK_NOT) $(
touch $@
.lib-dir-stamp:
- $(MKDIR_P) lib
+ $(MKDIR_P) lib unit
for i in $(CMDS); do $(LN_S) -f lvm-wrapper lib/$$i; done
for i in daemons/clvmd/clvmd daemons/dmeventd/dmeventd \
tools/dmsetup daemons/lvmetad/lvmetad \
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=327f62a25533c195f4b49…
Commit: 327f62a25533c195f4b49a29974f3c5a2f93c15b
Parent: b5f444d447e6c3f83fc1cc963b81501a760bbbeb
Author: David Teigland <teigland(a)redhat.com>
AuthorDate: Thu Jun 14 12:34:00 2018 -0500
Committer: David Teigland <teigland(a)redhat.com>
CommitterDate: Thu Jun 14 12:35:00 2018 -0500
man: update lvmsystemid wording
to refer to "shared VG" instead of "lockd VG".
---
man/lvmsystemid.7_main | 6 +++---
1 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/man/lvmsystemid.7_main b/man/lvmsystemid.7_main
index 9b36dc3..97c67c2 100644
--- a/man/lvmsystemid.7_main
+++ b/man/lvmsystemid.7_main
@@ -346,9 +346,9 @@ of the foreign VG to its own. See Overriding system ID above.
.SS shared VGs
-A shared/lockd VG has no system ID set, allowing multiple hosts to use it
-via lvmlockd. Changing a VG to a lockd type will clear the existing
-system ID. Applicable only if LVM is compiled with lockd support.
+A shared VG has no system ID set, allowing multiple hosts to use it
+via lvmlockd. Changing a VG to shared will clear the existing
+system ID. Applicable only if LVM is compiled with lvmlockd support.
.SS clustered VGs
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=b5f444d447e6c3f83fc1c…
Commit: b5f444d447e6c3f83fc1cc963b81501a760bbbeb
Parent: e84e9cd115c37a64de6c0b67972a01044dffe843
Author: David Teigland <teigland(a)redhat.com>
AuthorDate: Thu Jun 14 12:30:45 2018 -0500
Committer: David Teigland <teigland(a)redhat.com>
CommitterDate: Thu Jun 14 12:35:00 2018 -0500
man: updates to lvmlockd
The terminology has migrated toward using "shared VG"
rather than "lockd VG".
Also improve the wording in a number of places.
---
man/lvmlockd.8_main | 341 +++++++++++++++++++++-----------------------------
1 files changed, 143 insertions(+), 198 deletions(-)
diff --git a/man/lvmlockd.8_main b/man/lvmlockd.8_main
index 6411c01..cfb45b2 100644
--- a/man/lvmlockd.8_main
+++ b/man/lvmlockd.8_main
@@ -84,8 +84,8 @@ For default settings, see lvmlockd -h.
.SS Initial set up
-Using LVM with lvmlockd for the first time includes some one-time set up
-steps:
+Setting up LVM to use lvmlockd and a shared VG for the first time includes
+some one time set up steps:
.SS 1. choose a lock manager
@@ -94,7 +94,7 @@ steps:
If dlm (or corosync) are already being used by other cluster
software, then select dlm. dlm uses corosync which requires additional
configuration beyond the scope of this document. See corosync and dlm
-documentation for instructions on configuration, setup and usage.
+documentation for instructions on configuration, set up and usage.
.I sanlock
.br
@@ -117,7 +117,9 @@ Assign each host a unique host_id in the range 1-2000 by setting
.SS 3. start lvmlockd
-Use a unit/init file, or run the lvmlockd daemon directly:
+Start the lvmlockd daemon.
+.br
+Use systemctl, a cluster resource agent, or run directly, e.g.
.br
systemctl start lvm2-lvmlockd
@@ -125,14 +127,17 @@ systemctl start lvm2-lvmlockd
.I sanlock
.br
-Use unit/init files, or start wdmd and sanlock daemons directly:
+Start the sanlock and wdmd daemons.
+.br
+Use systemctl or run directly, e.g.
.br
systemctl start wdmd sanlock
.I dlm
.br
-Follow external clustering documentation when applicable, or use
-unit/init files:
+Start the dlm and corosync daemons.
+.br
+Use systemctl, a cluster resource agent, or run directly, e.g.
.br
systemctl start corosync dlm
@@ -141,18 +146,17 @@ systemctl start corosync dlm
vgcreate --shared <vgname> <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. lvmlockd will use the chosen lock manager.
+which lock manager is running. LVM commands acquire locks from lvmlockd,
+and lvmlockd uses the chosen lock manager.
.SS 6. start VG on all hosts
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. (A
-unit/init file can also be used to start VGs.)
+Shared VGs must be started before they are used. Starting the VG performs
+lock manager initialization that is necessary to begin using locks (i.e.
+creating and joining a lockspace). Starting the VG may take some time,
+and until the start completes the VG may not be modified or activated.
.SS 7. create and activate LVs
@@ -168,9 +172,9 @@ 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
-manager.
+After initial set up, start up and shut down include the following steps.
+They can be performed directly or may be automated using systemd or a
+cluster resource manager/agents.
\[bu]
start lvmlockd
@@ -204,106 +208,64 @@ stop lvmlockd
.SH TOPICS
-.SS VG access control
+.SS Protecting VGs on shared devices
-The following terms are used to describe different forms of VG access
-control.
+The following terms are used to describe the different ways of accessing
+VGs on shared devices.
-.I "lockd VG"
+.I "shared VG"
-A "lockd VG" is a shared VG that has a "lock type" of dlm or sanlock.
-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.
+A shared VG exists on shared storage that is visible to multiple hosts.
+LVM acquires locks through lvmlockd to coordinate access to shared VGs.
+A shared VG has lock_type "dlm" or "sanlock", which specifies the lock
+manager lvmlockd will use.
-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.
+When 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. In this
+situation, LVM commands are only allowed to read and display the VG;
+changes and activation will fail.
.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
-cannot be used concurrently from different hosts.
+A local VG is meant to be used by a single host. It has no lock type or
+lock type "none". 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
-host by having its system ID set, see
+host by having the system ID set, see
.BR lvmsystemid (7).
-Only the host with a matching system ID can use the local VG. A VG
-with no lock type and no system ID should be excluded from all but one
-host using lvm.conf filters. Without any of these protections, a local VG
-on shared devices can be easily damaged or destroyed.
+The host with a matching system ID can use the local VG and other hosts
+will ignore it. A VG with no lock type and no system ID should be
+excluded from all but one host using lvm.conf filters. Without any of
+these protections, a local VG on shared devices can be easily damaged or
+destroyed.
.I "clvm VG"
-A "clvm VG" is a VG on shared storage (like a lockd VG) that requires
-clvmd for clustering. See below for converting a clvm VG to a lockd VG.
+A clvm VG (or clustered VG) is a VG on shared storage (like a shared VG)
+that requires clvmd for clustering and locking. See below for converting
+a clvm/clustered VG to a shared VG.
-.SS lockd VGs from hosts not using lvmlockd
+.SS shared VGs from hosts not using lvmlockd
-Only hosts that use lockd VGs should be configured to run lvmlockd.
-However, shared devices in lockd VGs may be visible from hosts not
-using lvmlockd. From a host not using lvmlockd, lockd VGs are ignored
-in the same way as foreign VGs (see
+Hosts that do not use shared VGs will not be running lvmlockd. In this
+case, shared VGs that are still visible to the host will be ignored
+(like foreign VGs, see
.BR lvmsystemid (7).)
-The --shared option for reporting and display commands causes lockd VGs
+The --shared option for reporting and display commands causes shared VGs
to be displayed on a host not using lvmlockd, like the --foreign option
does for foreign VGs.
-.SS vgcreate comparison
-
-The type of VG access control is specified in the vgcreate command.
-See
-.BR vgcreate (8)
-for all vgcreate options.
-
-.B vgcreate <vgname> <devices>
-
-.IP \[bu] 2
-Creates a local VG with the local host's system ID when neither lvmlockd nor clvm are configured.
-.IP \[bu] 2
-Creates a local VG with the local host's system ID when lvmlockd is configured.
-.IP \[bu] 2
-Creates a clvm VG when clvm is configured.
-
-.P
-
-.B vgcreate --shared <vgname> <devices>
-.IP \[bu] 2
-Requires lvmlockd to be configured and running.
-.IP \[bu] 2
-Creates a lockd VG with lock type sanlock|dlm depending on which lock
-manager is running.
-.IP \[bu] 2
-LVM commands request locks from lvmlockd to use the VG.
-.IP \[bu] 2
-lvmlockd obtains locks from the selected lock manager.
-
-.P
-
-.B vgcreate -c|--clustered y <vgname> <devices>
-.IP \[bu] 2
-Requires clvm to be configured and running.
-.IP \[bu] 2
-Creates a clvm VG with the "clustered" flag.
-.IP \[bu] 2
-LVM commands request locks from clvmd to use the VG.
-
-.P
-
.SS creating the first sanlock VG
Creating the first sanlock VG is not protected by locking, so it requires
special attention. This is because sanlock locks exist on storage within
-the VG, so they are not available until the VG exists. The first sanlock
-VG created will automatically contain the "global lock". Be aware of the
-following special considerations:
+the VG, so they are not available until after the VG is created. The
+first sanlock VG that is created will automatically contain the "global
+lock". Be aware of the following special considerations:
.IP \[bu] 2
The first vgcreate command needs to be given the path to a device that has
@@ -318,54 +280,48 @@ to be accessible to all hosts that will use sanlock shared VGs. All hosts
will need to use the global lock from the first sanlock VG.
.IP \[bu] 2
-While running vgcreate for the first sanlock VG, ensure that the device
-being used is not used by another LVM command. Allocation of shared
-devices is usually protected by the global lock, but this cannot be done
-for the first sanlock VG which will hold the global lock.
-
-.IP \[bu] 2
-While running vgcreate for the first sanlock VG, ensure that the VG name
-being used is not used by another LVM command. Uniqueness of VG names is
-usually ensured by the global lock.
+The device and VG name used by the initial vgcreate will not be protected
+from concurrent use by another vgcreate on another host.
See below for more information about managing the sanlock global lock.
-.SS using lockd VGs
+.SS using shared VGs
-There are some special considerations when using lockd VGs.
+There are some special considerations when using shared VGs.
-When use_lvmlockd is first enabled in lvm.conf, 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.
+When use_lvmlockd is first enabled in lvm.conf, and before the first
+shared 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 shared VG is created,
+the global lock will be available, and LVM will be fully operational.
-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.
+When a new shared 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.
-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'.
+From the 'vgs' command, shared VGs are indicated by "s" (for shared) in
+the sixth attr field, and by "shared" in the "--options shared" report
+field. The specific lock type and lock args for a shared VG can be
+displayed with 'vgs -o+locktype,lockargs'.
-lockd VGs need to be "started" and "stopped", unlike other types of VGs.
+Shared VGs need to be "started" and "stopped", unlike other types of VGs.
See the following section for a full description of starting and stopping.
-vgremove of a lockd VG will fail if other hosts have the VG started.
-Run vgchange --lock-stop <vgname> on all other hosts before vgremove.
-(It may take several seconds before vgremove recognizes that all hosts
-have stopped a sanlock VG.)
+Removing a shared VG will fail if other hosts have the VG started. Run
+vgchange --lock-stop <vgname> on all other hosts before vgremove. (It may
+take several seconds before vgremove recognizes that all hosts have
+stopped a sanlock VG.)
.SS starting and stopping VGs
-Starting a lockd VG (vgchange --lock-start) causes the lock manager to
+Starting a shared 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 are allowed to
continue without locks (and with a warning).
-Stopping a lockd VG (vgchange --lock-stop) causes the lock manager to
+Stopping a shared 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.
@@ -374,7 +330,7 @@ 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:
+A shared VG can be started after all the following are true:
.br
\[bu]
lvmlockd is running
@@ -386,9 +342,9 @@ the lock manager is running
the VG's devices are visible on the system
.br
-A lockd VG can be stopped if all LVs are deactivated.
+A shared VG can be stopped if all LVs are deactivated.
-All lockd VGs can be started/stopped using:
+All shared VGs can be started/stopped using:
.br
vgchange --lock-start
.br
@@ -407,12 +363,12 @@ vgchange --lock-start --lock-opt nowait ...
lvmlockd can be asked directly to stop all lockspaces:
.br
-lvmlockctl --stop-lockspaces
+lvmlockctl -S|--stop-lockspaces
-To start only selected lockd VGs, use the lvm.conf
+To start only selected shared VGs, use the lvm.conf
activation/lock_start_list. When defined, only VG names in this list are
started by vgchange. If the list is not defined (the default), all
-visible lockd VGs are started. To start only "vg1", use the following
+visible shared VGs are started. To start only "vg1", use the following
lvm.conf configuration:
.nf
@@ -435,7 +391,7 @@ The "auto" option causes the command to follow the lvm.conf
activation/auto_lock_start_list. If auto_lock_start_list is undefined,
all VGs are started, just as if the auto option was not used.
-When auto_lock_start_list is defined, it lists the lockd VGs that should
+When auto_lock_start_list is defined, it lists the shared VGs that should
be started by the auto command. VG names that do not match an item in the
list will be ignored by the auto start command.
@@ -443,23 +399,20 @@ list will be ignored by the auto start command.
commands, i.e. with or without the auto option. When the lock_start_list
is defined, only VGs matching a list item can be started with vgchange.)
-The auto_lock_start_list allows a user to select certain lockd VGs that
+The auto_lock_start_list allows a user to select certain shared VGs that
should be automatically started by the system (or indirectly, those that
should not).
-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
To optimize the use of LVM with lvmlockd, be aware of the three kinds of
locks and when they are used:
-.I GL lock
+.I Global lock
-The global lock (GL lock) is associated with global information, which is
-information not isolated to a single VG. This includes:
+The global lock s associated with global information, which is information
+not isolated to a single VG. This includes:
\[bu]
The global VG namespace.
@@ -484,61 +437,58 @@ acquired.
.I VG lock
-A VG lock is associated with each lockd VG. The VG lock is acquired in
-shared mode to read the VG and in exclusive mode to change the VG (modify
-the VG metadata or activating LVs). This lock serializes access to a VG
-with all other LVM commands accessing the VG from all hosts.
-
-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.
+A VG lock is associated with each shared VG. The VG lock is acquired in
+shared mode to read the VG and in exclusive mode to change the VG or
+activate LVs. This lock serializes access to a VG with all other LVM
+commands accessing the VG from all hosts.
-The command 'vgs <vgname>' 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.
+The command 'vgs <vgname>' does not acquire the global lock (it does not
+need the list of all VG names), but will acquire the VG lock on each VG
+name argument.
.I LV lock
An LV lock is acquired before the LV is activated, and is released after
the LV is deactivated. If the LV lock cannot be acquired, the LV is not
-activated. LV locks are persistent and remain in place when the
-activation command is done. GL and VG locks are transient, and are held
-only while an LVM command is running.
+activated. (LV locks are persistent and remain in place when the
+activation command is done. Global and VG locks are transient, and are
+held only while an LVM command is running.)
.I lock 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.
+If a request for a Global 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.
.SS managing the global lock in sanlock VGs
The global lock exists in one of the sanlock VGs. The first sanlock VG
created will contain the global lock. Subsequent sanlock VGs will each
-contain disabled global locks that can be enabled later if necessary.
+contain a disabled global lock that can be enabled later if necessary.
The VG containing the global lock must be visible to all hosts using
-sanlock VGs. This can be a reason to create a small sanlock VG, visible
-to all hosts, and dedicated to just holding the global lock. While not
-required, this strategy can help to avoid difficulty in the future if VGs
-are moved or removed.
+sanlock VGs. For this reason, it can be useful to create a small sanlock
+VG, visible to all hosts, and dedicated to just holding the global lock.
+While not required, this strategy can help to avoid difficulty in the
+future if VGs 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
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.
+vgcreate determines that it's creating the first sanlock VG when no other
+sanlock VGs are visible on the system. It is possible that other sanlock
+VGs do exist, but are not visible when vgcreate checks for them. In this
+case, vgcreate will create a new sanlock VG with the global lock enabled.
+When the another VG containing a global lock appears, lvmlockd will then
+see more than one VG with a global lock enabled. LVM commands will report
+that there are duplicate global locks.
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
@@ -556,8 +506,8 @@ VGs with the command:
lvmlockctl --gl-enable <vgname>
-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.
+(Using a small sanlock VG dedicated to holding the global lock can avoid
+the case where the global lock must be manually enabled after a vgremove.)
.SS internal lvmlock LV
@@ -574,8 +524,8 @@ device, then use vgextend to add other devices.
.SS LV activation
-In a shared VG, activation changes involve locking through lvmlockd, and
-the following values are possible with lvchange/vgchange -a:
+In a shared VG, LV activation involves locking through lvmlockd, and the
+following values are possible with lvchange/vgchange -a:
.IP \fBy\fP|\fBey\fP
The command activates the LV in exclusive mode, allowing a single host
@@ -596,10 +546,6 @@ The shared mode is intended for a multi-host/cluster application or
file system.
LV types that cannot be used concurrently
from multiple hosts include thin, cache, raid, and snapshot.
-lvextend on LV with shared locks is not yet allowed. The LV must be
-deactivated, or activated exclusively to run lvextend. (LVs with
-the mirror type can be activated in shared mode from multiple hosts
-when using the dlm lock type and cmirrord.)
.IP \fBn\fP
The command deactivates the LV. After deactivating the LV, the command
@@ -654,7 +600,7 @@ with the expiring lease before other hosts can acquire its locks.
When the sanlock daemon detects that the lease storage is lost, it runs
the command lvmlockctl --kill <vgname>. This command emits a syslog
-message stating that lease storage is lost for the VG and LVs must be
+message stating that lease storage is lost for the VG, and LVs must be
immediately deactivated.
If no LVs are active in the VG, then the lockspace with an expiring lease
@@ -666,10 +612,10 @@ If the VG has active LVs when the lock storage is lost, 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 machine reset is effectively 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
+about 40 seconds, sanlock uses wdmd and the local watchdog to reset the
+host. The machine reset is effectively 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.
In the future, the lvmlockctl kill command may automatically attempt to
@@ -681,8 +627,7 @@ sanlock resets the machine.
If the sanlock daemon fails or exits while a lockspace is started, the
local watchdog will reset the host. This is necessary to protect any
-application resources that depend on sanlock leases which will be lost
-without sanlock running.
+application resources that depend on sanlock leases.
.SS changing dlm cluster name
@@ -762,14 +707,14 @@ Start the VG on hosts to use it:
vgchange --lock-start <vgname>
-.SS changing a local VG to a lockd VG
+.SS changing a local VG to a shared VG
All LVs must be inactive to change the lock type.
lvmlockd must be configured and running as described in USAGE.
.IP \[bu] 2
-Change a local VG to a lockd VG with the command:
+Change a local VG to a shared VG with the command:
.br
vgchange --lock-type sanlock|dlm <vgname>
@@ -780,7 +725,7 @@ vgchange --lock-start <vgname>
.P
-.SS changing a lockd VG to a local VG
+.SS changing a shared VG to a local VG
All LVs must be inactive to change the lock type.
@@ -806,11 +751,11 @@ type can be forcibly changed to none with:
vgchange --lock-type none --lock-opt force <vgname>
-To change a VG from one lockd type to another (i.e. between sanlock and
+To change a VG from one lock type to another (i.e. between sanlock and
dlm), first change it to a local VG, then to the new type.
-.SS changing a clvm VG to a lockd VG
+.SS changing a clvm/clustered VG to a shared VG
All LVs must be inactive to change the lock type.
@@ -823,15 +768,15 @@ If the clvm cluster is no longer running on any nodes, then extra options
can be used to forcibly make the VG local. Caution: this is only safe if
all nodes have stopped using the VG:
-vgchange --lock-type none --lock-opt force <vgname>
+vgchange --lock-type none --lock-opt force <vgname>
After the VG is local, follow the steps described in "changing a local VG
-to a lockd VG".
+to a shared VG".
-.SS limitations of lockd VGs
+.SS limitations of shared VGs
-Things that do not yet work in lockd VGs:
+Things that do not yet work in shared VGs:
.br
\[bu]
using external origins for thin LVs
@@ -851,22 +796,22 @@ vgsplit and vgmerge (convert to a local VG to do this)
.SS lvmlockd changes from clvmd
-(See above for converting an existing clvm VG to a lockd VG.)
+(See above for converting an existing clvm VG to a shared VG.)
While lvmlockd and clvmd are entirely different systems, LVM command usage
remains similar. Differences are more notable when using lvmlockd's
sanlock option.
-Visible usage differences between lockd VGs (using lvmlockd) and clvm VGs
-(using clvmd):
+Visible usage differences between shared VGs (using lvmlockd) and
+clvm/clustered VGs (using clvmd):
.IP \[bu] 2
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 shared VG, and vgcreate --clustered y
+creates a clvm/clustered VG.
.IP \[bu] 2
lvmlockd adds the option of using sanlock for locking, avoiding the
@@ -887,11 +832,11 @@ lvmlockd works with thin and cache pools and LVs.
lvmlockd works with lvmetad.
.IP \[bu] 2
-lvmlockd saves the cluster name for a lockd VG using dlm. Only hosts in
+lvmlockd saves the cluster name for a shared 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
+lvmlockd requires starting/stopping shared VGs with vgchange --lock-start
and --lock-stop.
.IP \[bu] 2
@@ -914,7 +859,7 @@ 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.
+for shared VGs.
.IP \[bu] 2
If lvmlockd fails or is killed while in use, locks it held remain but are
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=e84e9cd115c37a64de6c0…
Commit: e84e9cd115c37a64de6c0b67972a01044dffe843
Parent: fededfbbbc5ed79c509a34a0f8a681fa27e49533
Author: Joe Thornber <ejt(a)redhat.com>
AuthorDate: Thu Jun 14 14:32:17 2018 +0100
Committer: Joe Thornber <ejt(a)redhat.com>
CommitterDate: Thu Jun 14 14:32:17 2018 +0100
device_mapper: remove libdm-stats.c
We don't use it in lvm.
---
device_mapper/Makefile | 1 -
device_mapper/all.h | 1314 -----------
device_mapper/libdm-stats.c | 5096 -------------------------------------------
3 files changed, 0 insertions(+), 6411 deletions(-)
diff --git a/device_mapper/Makefile b/device_mapper/Makefile
index 3a1e415..931431c 100644
--- a/device_mapper/Makefile
+++ b/device_mapper/Makefile
@@ -17,7 +17,6 @@ DEVICE_MAPPER_SOURCE=\
device_mapper/libdm-deptree.c \
device_mapper/libdm-file.c \
device_mapper/libdm-report.c \
- device_mapper/libdm-stats.c \
device_mapper/libdm-string.c \
device_mapper/libdm-targets.c \
device_mapper/libdm-timestamp.c \
diff --git a/device_mapper/all.h b/device_mapper/all.h
index c834f32..8dcb38c 100644
--- a/device_mapper/all.h
+++ b/device_mapper/all.h
@@ -436,1006 +436,6 @@ 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
- * and obtaining and manipulating current counter values from the
- * kernel. Methods are provided to return baisc count values and to
- * derive time-based metrics when a suitable interval estimate is
- * provided.
- *
- * Internally the dm_stats handle contains a pointer to a table of one
- * or more dm_stats_region objects representing the regions registered
- * with the dm_stats_create_region() method. These in turn point to a
- * table of one or more dm_stats_counters objects containing the
- * counter sets for each defined area within the region:
- *
- * dm_stats->dm_stats_region[nr_regions]->dm_stats_counters[nr_areas]
- *
- * This structure is private to the library and may change in future
- * versions: all users should make use of the public interface and treat
- * the dm_stats type as an opaque handle.
- *
- * Regions and counter sets are stored in order of increasing region_id.
- * Depending on region specifications and the sequence of create and
- * delete operations this may not correspond to increasing sector
- * number: users of the library should not assume that this is the case
- * unless region creation is deliberately managed to ensure this (by
- * always creating regions in strict order of ascending sector address).
- *
- * Regions may also overlap so the same sector range may be included in
- * more than one region or area: applications should be prepared to deal
- * with this or manage regions such that it does not occur.
- */
-struct dm_stats;
-
-/*
- * Histogram handle.
- *
- * A histogram object represents the latency histogram values and bin
- * boundaries of the histogram associated with a particular area.
- *
- * Operations on the handle allow the number of bins, bin boundaries,
- * counts and relative proportions to be obtained as well as the
- * conversion of a histogram or its bounds to a compact string
- * representation.
- */
-struct dm_histogram;
-
-/*
- * 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.
- *
- * If program_id is NULL or the empty string a program_id will be
- * automatically set to the value contained in /proc/self/comm.
- */
-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);
-
-/*
- * Bind a dm_stats handle to the device backing the file referenced
- * by the specified file descriptor.
- *
- * File descriptor fd must reference a regular file, open for reading,
- * in a local file system, backed by a device-mapper device, that
- * supports the FIEMAP ioctl, and that returns data describing the
- * physical location of extents.
- */
-int dm_stats_bind_from_fd(struct dm_stats *dms, int fd);
-/*
- * Test whether the running kernel supports the precise_timestamps
- * feature. Presence of this feature also implies histogram support.
- * The library will check this call internally and fails any attempt
- * to use nanosecond counters or histograms on kernels that fail to
- * meet this check.
- */
-int dm_message_supports_precise_timestamps(void);
-
-/*
- * Precise timetamps and histogram support.
- *
- * Test for the presence of precise_timestamps and histogram support.
- */
-int dm_stats_driver_supports_precise(void);
-int dm_stats_driver_supports_histogram(void);
-
-/*
- * Returns 1 if the specified region has the precise_timestamps feature
- * enabled (i.e. produces nanosecond-precision counter values) or 0 for
- * a region using the default milisecond precision.
- */
-int dm_stats_get_region_precise_timestamps(const struct dm_stats *dms,
- uint64_t region_id);
-
-/*
- * Returns 1 if the region at the current cursor location has the
- * precise_timestamps feature enabled (i.e. produces
- * nanosecond-precision counter values) or 0 for a region using the
- * default milisecond precision.
- */
-int dm_stats_get_current_region_precise_timestamps(const struct dm_stats *dms);
-
-#define DM_STATS_ALL_PROGRAMS ""
-/*
- * Parse the response from a @stats_list message. dm_stats_list will
- * allocate the necessary dm_stats and dm_stats region structures from
- * the embedded dm_pool. No counter data will be obtained (the counters
- * members of dm_stats_region objects are set to NULL).
- *
- * 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
- * the argument is NULL then the default program_id associated with the
- * dm_stats handle will be used. Passing the special value
- * DM_STATS_ALL_PROGRAMS will cause all regions to be queried
- * regardless of region program_id.
- */
-int dm_stats_list(struct dm_stats *dms, const char *program_id);
-
-#define DM_STATS_REGIONS_ALL UINT64_MAX
-/*
- * 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
- * the argument is NULL then the default program_id associated with the
- * dm_stats handle will be used. Passing the special value
- * DM_STATS_ALL_PROGRAMS will cause all regions to be queried
- * regardless of region program_id.
- *
- * Passing the special value DM_STATS_REGIONS_ALL as the region_id
- * argument will attempt to retrieve all regions selected by the
- * program_id argument.
- *
- * 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.
- * Passing zero for both start and len will create a region spanning
- * the entire device.
- *
- * Step determines how to subdivide the region into discrete counter
- * sets: a positive value specifies the size of areas into which the
- * region should be split while a negative value will split the region
- * into a number of areas equal to the absolute value of step:
- *
- * - a region with one area spanning the entire device:
- *
- * dm_stats_create_region(dms, 0, 0, -1, p, a);
- *
- * - a region with areas of 1MiB:
- *
- * dm_stats_create_region(dms, 0, 0, 1 << 11, p, a);
- *
- * - one 1MiB region starting at 1024 sectors with two areas:
- *
- * dm_stats_create_region(dms, 1024, 1 << 11, -2, p, a);
- *
- * If precise is non-zero attempt to create a region with nanosecond
- * precision counters using the kernel precise_timestamps feature.
- *
- * precise - A flag to request nanosecond precision counters
- * to be used for this region.
- *
- * histogram_bounds - specify the boundaries of a latency histogram to
- * be tracked for the region. The values are expressed as an array of
- * uint64_t terminated with a zero. Values must be in order of ascending
- * magnitude and specify the upper bounds of successive histogram bins
- * in nanoseconds (with an implicit lower bound of zero on the first bin
- * and an implicit upper bound of infinity on the final bin). For
- * example:
- *
- * uint64_t bounds_ary[] = { 1000, 2000, 3000, 0 };
- *
- * Specifies a histogram with four bins: 0-1000ns, 1000-2000ns,
- * 2000-3000ns and >3000ns.
- *
- * The smallest latency value that can be tracked for a region not using
- * precise_timestamps is 1ms: attempting to create a region with
- * histogram boundaries < 1ms will cause the precise_timestamps feature
- * to be enabled for that region automatically if it was not requested
- * explicitly.
- *
- * 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.
- *
- * user_data is an optional string argument that is added to the
- * content of the aux_data field stored with the statistics region by
- * the kernel.
- *
- * The library may also use this space internally, for example, to
- * store a group descriptor or other metadata: in this case the
- * library will strip any internal data fields from the value before
- * it is returned via a call to dm_stats_get_region_aux_data().
- *
- * The user data stored is not accessed by the library or kernel and
- * may be used to store an arbitrary data word (embedded whitespace is
- * not permitted).
- *
- * An application using both the library and direct access to the
- * @stats_list device-mapper message may see the internal values stored
- * in this field by the library. In such cases any string up to and
- * including the first '#' in the field must be treated as an opaque
- * value and preserved across any external modification of aux_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,
- int precise, struct dm_histogram *bounds,
- const char *program_id, const char *user_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.
- */
-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).
- *
- * 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
- * numeric form accessible via the dm_stats_get_*() counter access
- * methods.
- *
- * A subset of the data lines may be requested by setting the
- * 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).
- */
-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
- * 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).
- *
- * Always returns zero on an empty handle.
- */
-uint64_t dm_stats_get_nr_regions(const struct dm_stats *dms);
-
-/*
- * Determine the number of groups contained in a dm_stats handle
- * following a dm_stats_list() or dm_stats_populate() call.
- *
- * The value returned is the number of registered groups visible with the
- * progam_id value used for the list or populate operation and may not be
- * equal to the highest present group_id (either due to program_id
- * filtering or gaps in the sequence of group_id values).
- *
- * Always returns zero on an empty handle.
- */
-uint64_t dm_stats_get_nr_groups(const struct dm_stats *dms);
-
-/*
- * Test whether region_id is present in this dm_stats handle.
- */
-int dm_stats_region_present(const 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(const struct dm_stats *dms,
- uint64_t region_id);
-
-/*
- * Returns the total number of areas (counter sets) in all regions of the
- * given dm_stats object.
- */
-uint64_t dm_stats_get_nr_areas(const struct dm_stats *dms);
-
-/*
- * Test whether group_id is present in this dm_stats handle.
- */
-int dm_stats_group_present(const struct dm_stats *dms, uint64_t group_id);
-
-/*
- * Return the number of bins in the histogram configuration for the
- * specified region or zero if no histogram specification is configured.
- * Valid following a dm_stats_list() or dm_stats_populate() operation.
- */
-int dm_stats_get_region_nr_histogram_bins(const struct dm_stats *dms,
- uint64_t region_id);
-
-/*
- * Parse a histogram string with optional unit suffixes into a
- * dm_histogram bounds description.
- *
- * A histogram string is a string of numbers "n1,n2,n3,..." that
- * represent the boundaries of a histogram. The first and final bins
- * have implicit lower and upper bounds of zero and infinity
- * respectively and boundary values must occur in order of ascending
- * magnitude. Unless a unit suffix is given all values are specified in
- * nanoseconds.
- *
- * For example, if bounds_str="300,600,900", the region will be created
- * with a histogram containing four bins. Each report will include four
- * numbers a:b:c:d. a is the number of requests that took between 0 and
- * 300ns to complete, b is the number of requests that took 300-600ns to
- * complete, c is the number of requests that took 600-900ns to complete
- * and d is the number of requests that took more than 900ns to
- * complete.
- *
- * An optional unit suffix of 's', 'ms', 'us', or 'ns' may be used to
- * specify units of seconds, miliseconds, microseconds, or nanoseconds:
- *
- * bounds_str="1ns,1us,1ms,1s"
- * bounds_str="500us,1ms,1500us,2ms"
- * bounds_str="200ms,400ms,600ms,800ms,1s"
- *
- * The smallest valid unit of time for a histogram specification depends
- * on whether the region uses precise timestamps: for a region with the
- * default milisecond precision the smallest possible histogram boundary
- * magnitude is one milisecond: attempting to use a histogram with a
- * boundary less than one milisecond when creating a region will cause
- * the region to be created with the precise_timestamps feature enabled.
- *
- * On sucess a pointer to the struct dm_histogram representing the
- * bounds values is returned, or NULL in the case of error. The returned
- * pointer should be freed using free() when no longer required.
- */
-struct dm_histogram *dm_histogram_bounds_from_string(const char *bounds_str);
-
-/*
- * Parse a zero terminated array of uint64_t into a dm_histogram bounds
- * description.
- *
- * Each value in the array specifies the upper bound of a bin in the
- * latency histogram in nanoseconds. Values must appear in ascending
- * order of magnitude.
- *
- * The smallest valid unit of time for a histogram specification depends
- * on whether the region uses precise timestamps: for a region with the
- * default milisecond precision the smallest possible histogram boundary
- * magnitude is one milisecond: attempting to use a histogram with a
- * boundary less than one milisecond when creating a region will cause
- * the region to be created with the precise_timestamps feature enabled.
- */
-struct dm_histogram *dm_histogram_bounds_from_uint64(const uint64_t *bounds);
-
-/*
- * Destroy the histogram bounds array obtained from a call to
- * dm_histogram_bounds_from_string().
- */
-void dm_histogram_bounds_destroy(struct dm_histogram *bounds);
-
-/*
- * Destroy a dm_stats object and all associated regions, counter
- * sets and histograms.
- */
-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.
- *
- * The interval is used to calculate time-based metrics from the basic
- * counter data: an interval must be set before calling any of the
- * metric methods.
- *
- * For best accuracy the duration should be measured and updated at the
- * 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.
- */
-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);
-
-/*
- * Retrieve the configured sampling interval in either nanoseconds or
- * milliseconds.
- */
-uint64_t dm_stats_get_sampling_interval_ns(const struct dm_stats *dms);
-uint64_t dm_stats_get_sampling_interval_ms(const 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.
- *
- * Use with caution! Most users of the library should set a valid,
- * non-NULL program_id for every statistics region created. Failing to
- * do so may result in confusing state when multiple programs are
- * creating and managing statistics regions.
- *
- * All users of the library are encouraged to choose an unambiguous,
- * unique program_id: this could be based on PID (for programs that
- * create, report, and delete regions in a single process), session id,
- * executable name, or some other distinguishing string.
- *
- * Use of the empty string as a program_id does not simplify use of the
- * library or the command line tools and use of this value is strongly
- * discouraged.
- */
-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.
- *
- * All values are returned in units of 512b sectors.
- */
-int dm_stats_get_region_start(const struct dm_stats *dms, uint64_t *start,
- uint64_t region_id);
-
-int dm_stats_get_region_len(const struct dm_stats *dms, uint64_t *len,
- uint64_t region_id);
-
-int dm_stats_get_region_area_len(const struct dm_stats *dms,
- uint64_t *len, uint64_t region_id);
-
-/*
- * Area properties: start, offset 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 of an area is a function of the area_id and the containing
- * region's start and area length: it gives the absolute offset into the
- * containing device of the beginning of the area.
- *
- * The offset expresses the area's relative offset into the current
- * region. I.e. the area start minus the start offset of the containing
- * region.
- *
- * All values are returned in units of 512b sectors.
- */
-int dm_stats_get_area_start(const struct dm_stats *dms, uint64_t *start,
- uint64_t region_id, uint64_t area_id);
-
-int dm_stats_get_area_offset(const struct dm_stats *dms, uint64_t *offset,
- uint64_t region_id, uint64_t area_id);
-
-/*
- * Retrieve program_id and user aux_data for a specific region.
- *
- * Only valid following a call to dm_stats_list().
- */
-
-/*
- * Retrieve program_id for the specified region.
- *
- * 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.
- */
-const char *dm_stats_get_region_program_id(const struct dm_stats *dms,
- uint64_t region_id);
-
-/*
- * Retrieve user aux_data set for the specified region. This function
- * will return any stored user aux_data as a string in the memory
- * pointed to by the aux_data argument.
- *
- * Any library internal aux_data fields, such as DMS_GROUP descriptors,
- * are stripped before the value is returned.
- *
- * 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.
- */
-const char *dm_stats_get_region_aux_data(const struct dm_stats *dms,
- uint64_t region_id);
-
-typedef enum {
- DM_STATS_OBJECT_TYPE_NONE,
- DM_STATS_OBJECT_TYPE_AREA,
- DM_STATS_OBJECT_TYPE_REGION,
- DM_STATS_OBJECT_TYPE_GROUP
-} dm_stats_obj_type_t;
-
-/*
- * Statistics cursor
- *
- * A dm_stats handle maintains an optional cursor into the statistics
- * tables that it stores. Iterators are provided to visit each region,
- * area, or group in a handle and accessor methods are provided to
- * obtain properties and values for the object at the current cursor
- * position.
- *
- * Using the cursor simplifies walking all regions or groups when
- * the tables are sparse (i.e. contains some present and some
- * non-present region_id or group_id values either due to program_id
- * filtering or the ordering of region and group creation and deletion).
- *
- * Simple macros are provided to visit each area, region, or group,
- * contained in a handle and applications are encouraged to use these
- * where possible.
- */
-
-/*
- * Walk flags are used to initialise a dm_stats handle's cursor control
- * and to select region or group aggregation when calling a metric or
- * counter property method with immediate group, region, and area ID
- * values.
- *
- * Walk flags are stored in the uppermost word of a uint64_t so that
- * a region_id or group_id may be encoded in the lower bits. This
- * allows an aggregate region_id or group_id to be specified when
- * retrieving counter or metric values.
- *
- * Flags may be ORred together when used to initialise a dm_stats_walk:
- * the resulting walk will visit instance of each type specified by
- * the flag combination.
- */
-#define DM_STATS_WALK_AREA 0x1000000000000ULL
-#define DM_STATS_WALK_REGION 0x2000000000000ULL
-#define DM_STATS_WALK_GROUP 0x4000000000000ULL
-
-#define DM_STATS_WALK_ALL 0x7000000000000ULL
-#define DM_STATS_WALK_DEFAULT (DM_STATS_WALK_AREA | DM_STATS_WALK_REGION)
-
-/*
- * Skip regions from a DM_STATS_WALK_REGION that contain only a single
- * area: in this case the region's aggregate values are identical to
- * the values of the single contained area. Setting this flag will
- * suppress these duplicate entries during a dm_stats_walk_* with the
- * DM_STATS_WALK_REGION flag set.
- */
-#define DM_STATS_WALK_SKIP_SINGLE_AREA 0x8000000000000ULL
-
-/*
- * Initialise the cursor control of a dm_stats handle for the specified
- * walk type(s). Including a walk flag in the flags argument will cause
- * any subsequent walk to visit that type of object (until the next
- * call to dm_stats_walk_init()).
- */
-int dm_stats_walk_init(struct dm_stats *dms, uint64_t flags);
-
-/*
- * Set the cursor of a dm_stats handle to address the first present
- * group, region, or area of the currently configured walk. 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. If the end of
- * the region, area, or group tables is reached a subsequent call to
- * dm_stats_walk_end() will return 1 and dm_stats_object_type() called
- * on the location will return DM_STATS_OBJECT_TYPE_NONE,
- */
-void dm_stats_walk_next(struct dm_stats *dms);
-
-/*
- * Force the statistics cursor to advance to the next region. This will
- * stop any in-progress area walk (by clearing DM_STATS_WALK_AREA) and
- * advance the cursor to the next present region, the first present
- * group (if DM_STATS_GROUP_WALK is set), or to the end. In this case a
- * subsequent call to dm_stats_walk_end() will return 1 and a call to
- * dm_stats_object_type() for the location will return
- * DM_STATS_OBJECT_TYPE_NONE.
- */
-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);
-
-/*
- * Return the type of object at the location specified by 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 the
- * corresponding region or area identifier will be taken from the
- * current cursor location. If the cursor location or the value encoded
- * by region_id and area_id indicates an aggregate region or group,
- * this will be reflected in the value returned.
- */
-dm_stats_obj_type_t dm_stats_object_type(const struct dm_stats *dms,
- uint64_t region_id,
- uint64_t area_id);
-
-/*
- * Return the type of object at the current stats cursor location.
- */
-dm_stats_obj_type_t dm_stats_current_object_type(const 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_init((dms), DM_STATS_WALK_REGION), \
- 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_init((dms), DM_STATS_WALK_AREA), \
- dm_stats_walk_start((dms)); \
- !dm_stats_walk_end((dms)); dm_stats_walk_next((dms)))
-
-/*
- * Iterate over the regions table visiting each group. Metric and
- * counter methods will return values for the group.
- *
- * If the group table is empty or unpopulated the loop body will not
- * be executed.
- */
-#define dm_stats_foreach_group(dms) \
-for (dm_stats_walk_init((dms), DM_STATS_WALK_GROUP), \
- 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) \
-do { \
- dm_stats_walk_start((dms)); \
- do
-
-/*
- * Start a 'while' style loop or end a 'do..while' loop iterating over the
- * regions contained in dm_stats handle 'dms'.
- */
-#define dm_stats_walk_while(dms) \
- while(!dm_stats_walk_end((dms))); \
-} while (0)
-
-/*
- * 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(const struct dm_stats *dms);
-
-/*
- * Retrieve the current values of the stats cursor.
- */
-uint64_t dm_stats_get_current_region(const struct dm_stats *dms);
-uint64_t dm_stats_get_current_area(const 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.
- */
-int dm_stats_get_current_region_start(const struct dm_stats *dms,
- uint64_t *start);
-
-int dm_stats_get_current_region_len(const struct dm_stats *dms,
- uint64_t *len);
-
-int dm_stats_get_current_region_area_len(const 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.
- */
-int dm_stats_get_current_area_start(const struct dm_stats *dms,
- uint64_t *start);
-
-int dm_stats_get_current_area_offset(const struct dm_stats *dms,
- uint64_t *offset);
-
-int dm_stats_get_current_area_len(const 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(const struct dm_stats *dms);
-
-/*
- * Return a pointer to the user aux_data string for the region at the
- * current cursor location.
- */
-const char *dm_stats_get_current_region_aux_data(const struct dm_stats *dms);
-
-/*
- * Statistics groups and data aggregation.
- */
-
-/*
- * Create a new group in stats handle dms from the group descriptor
- * passed in group. The group descriptor is a string containing a list
- * of region_id values that will be included in the group. The first
- * region_id found will be the group leader. Ranges of identifiers may
- * be expressed as "M-N", where M and N are the start and end region_id
- * values for the range.
- */
-int dm_stats_create_group(struct dm_stats *dms, const char *group,
- const char *alias, uint64_t *group_id);
-
-/*
- * Remove the specified group_id. If the remove argument is zero the
- * group will be removed but the regions that it contained will remain.
- * If remove is non-zero then all regions that belong to the group will
- * also be removed.
- */
-int dm_stats_delete_group(struct dm_stats *dms, uint64_t group_id, int remove);
-
-/*
- * Set an alias for this group or region. The alias will be returned
- * instead of the normal dm-stats name for this region or group.
- */
-int dm_stats_set_alias(struct dm_stats *dms, uint64_t group_id,
- const char *alias);
-
-/*
- * Returns a pointer to the currently configured alias for id, or the
- * name of the dm device the handle is bound to if no alias has been
- * set. The pointer will be freed automatically when a new alias is set
- * or when the stats handle is cleared.
- */
-const char *dm_stats_get_alias(const struct dm_stats *dms, uint64_t id);
-
-#define DM_STATS_GROUP_NONE UINT64_MAX
-/*
- * Return the group_id that the specified region_id belongs to, or the
- * special value DM_STATS_GROUP_NONE if the region does not belong
- * to any group.
- */
-uint64_t dm_stats_get_group_id(const struct dm_stats *dms, uint64_t region_id);
-
-/*
- * Store a pointer to a string describing the regions that are members
- * of the group specified by group_id in the memory pointed to by buf.
- * The string is in the same format as the 'group' argument to
- * dm_stats_create_group().
- *
- * The pointer does not need to be freed explicitly by the caller: it
- * will become invalid following a subsequent dm_stats_list(),
- * dm_stats_populate() or dm_stats_destroy() of the corresponding
- * dm_stats handle.
- */
-int dm_stats_get_group_descriptor(const struct dm_stats *dms,
- uint64_t group_id, char **buf);
-
-/*
- * Create regions that correspond to the extents of a file in the
- * filesystem and optionally place them into a group.
- *
- * File descriptor fd must reference a regular file, open for reading,
- * in a local file system that supports the FIEMAP ioctl, and that
- * returns data describing the physical location of extents.
- *
- * The file descriptor can be closed by the caller following the call
- * to dm_stats_create_regions_from_fd().
- *
- * Unless nogroup is non-zero the regions will be placed into a group
- * and the group alias set to the value supplied (if alias is NULL no
- * group alias will be assigned).
- *
- * On success the function returns a pointer to an array of uint64_t
- * containing the IDs of the newly created regions. The region_id
- * array is terminated by the value DM_STATS_REGION_NOT_PRESENT and
- * should be freed using free() when no longer required.
- *
- * On error NULL is returned.
- *
- * Following a call to dm_stats_create_regions_from_fd() the handle
- * is guaranteed to be in a listed state, and to contain any region
- * and group identifiers created by the operation.
- *
- * The group_id for the new group is equal to the region_id value in
- * the first array element.
- */
-uint64_t *dm_stats_create_regions_from_fd(struct dm_stats *dms, int fd,
- int group, int precise,
- struct dm_histogram *bounds,
- const char *alias);
-/*
- * Update a group of regions that correspond to the extents of a file
- * in the filesystem, adding and removing regions to account for
- * allocation changes in the underlying file.
- *
- * File descriptor fd must reference a regular file, open for reading,
- * in a local file system that supports the FIEMAP ioctl, and that
- * returns data describing the physical location of extents.
- *
- * The file descriptor can be closed by the caller following the call
- * to dm_stats_update_regions_from_fd().
- *
- * On success the function returns a pointer to an array of uint64_t
- * containing the IDs of the updated regions (including any existing
- * regions that were not modified by the call).
- *
- * The region_id array is terminated by the special value
- * DM_STATS_REGION_NOT_PRESENT and should be freed using free()
- * when no longer required.
- *
- * On error NULL is returned.
- *
- * Following a call to dm_stats_update_regions_from_fd() the handle
- * is guaranteed to be in a listed state, and to contain any region
- * and group identifiers created by the operation.
- *
- * This function cannot be used with file mapped regions that are
- * not members of a group: either group the regions, or remove them
- * and re-map them with dm_stats_create_regions_from_fd().
- */
-uint64_t *dm_stats_update_regions_from_fd(struct dm_stats *dms, int fd,
- uint64_t group_id);
-
-
-/*
- * The file map monitoring daemon can monitor files in two distinct
- * ways: the mode affects the behaviour of the daemon when a file
- * under monitoring is renamed or unlinked, and the conditions which
- * cause the daemon to terminate.
- *
- * In both modes, the daemon will always shut down when the group
- * being monitored is deleted.
- *
- * Follow inode:
- * The daemon follows the inode of the file, as it was at the time the
- * daemon started. The file descriptor referencing the file is kept
- * open at all times, and the daemon will exit when it detects that
- * the file has been unlinked and it is the last holder of a reference
- * to the file.
- *
- * This mode is useful if the file is expected to be renamed, or moved
- * within the file system, while it is being monitored.
- *
- * Follow path:
- * The daemon follows the path that was given on the daemon command
- * line. The file descriptor referencing the file is re-opened on each
- * iteration of the daemon, and the daemon will exit if no file exists
- * at this location (a tolerance is allowed so that a brief delay
- * between unlink() and creat() is permitted).
- *
- * This mode is useful if the file is updated by unlinking the original
- * and placing a new file at the same path.
- */
-
-typedef enum {
- DM_FILEMAPD_FOLLOW_INODE,
- DM_FILEMAPD_FOLLOW_PATH,
- DM_FILEMAPD_FOLLOW_NONE
-} dm_filemapd_mode_t;
-
-/*
- * Parse a string representation of a dmfilemapd mode.
- *
- * Returns a valid dm_filemapd_mode_t value on success, or
- * DM_FILEMAPD_FOLLOW_NONE on error.
- */
-dm_filemapd_mode_t dm_filemapd_mode_from_string(const char *mode_str);
-
-/*
- * Start the dmfilemapd filemap monitoring daemon for the specified
- * file descriptor, group, and file system path. The daemon will
- * monitor the file for allocation changes, and when a change is
- * detected, call dm_stats_update_regions_from_fd() to update the
- * mapped regions for the file.
- *
- * The path provided to dm_stats_start_filemapd() must be an absolute
- * path, and should reflect the path of 'fd' at the time that it was
- * opened.
- *
- * The mode parameter controls the behaviour of the daemon when the
- * file being monitored is unlinked or moved: see the comments for
- * dm_filemapd_mode_t for a full description and possible values.
- *
- * The daemon can be stopped at any time by sending SIGTERM to the
- * daemon pid.
- */
-int dm_stats_start_filemapd(int fd, uint64_t group_id, const char *path,
- dm_filemapd_mode_t mode, unsigned foreground,
- unsigned verbose);
-
-/*
* Call this to actually run the ioctl.
*/
int dm_task_run(struct dm_task *dmt);
@@ -2840,320 +1840,6 @@ int dm_report_group_pop(struct dm_report_group *group);
int dm_report_group_output_and_pop_all(struct dm_report_group *group);
int dm_report_group_destroy(struct dm_report_group *group);
-/*
- * Stats counter access methods
- *
- * Each method returns the corresponding stats counter value from 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.
- *
- * Two methods are provided to access counter values: a named function
- * for each available counter field and a single function that accepts
- * an enum value specifying the required field. New code is encouraged
- * to use the enum based interface as calls to the named functions are
- * implemented using the enum method internally.
- *
- * 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
-#define DM_STATS_AREA_CURRENT UINT64_MAX
-
-typedef enum {
- DM_STATS_READS_COUNT,
- DM_STATS_READS_MERGED_COUNT,
- DM_STATS_READ_SECTORS_COUNT,
- DM_STATS_READ_NSECS,
- DM_STATS_WRITES_COUNT,
- DM_STATS_WRITES_MERGED_COUNT,
- DM_STATS_WRITE_SECTORS_COUNT,
- DM_STATS_WRITE_NSECS,
- DM_STATS_IO_IN_PROGRESS_COUNT,
- DM_STATS_IO_NSECS,
- DM_STATS_WEIGHTED_IO_NSECS,
- DM_STATS_TOTAL_READ_NSECS,
- DM_STATS_TOTAL_WRITE_NSECS,
- DM_STATS_NR_COUNTERS
-} dm_stats_counter_t;
-
-uint64_t dm_stats_get_counter(const struct dm_stats *dms,
- dm_stats_counter_t counter,
- uint64_t region_id, uint64_t area_id);
-
-uint64_t dm_stats_get_reads(const struct dm_stats *dms,
- uint64_t region_id, uint64_t area_id);
-
-uint64_t dm_stats_get_reads_merged(const struct dm_stats *dms,
- uint64_t region_id, uint64_t area_id);
-
-uint64_t dm_stats_get_read_sectors(const struct dm_stats *dms,
- uint64_t region_id, uint64_t area_id);
-
-uint64_t dm_stats_get_read_nsecs(const struct dm_stats *dms,
- uint64_t region_id, uint64_t area_id);
-
-uint64_t dm_stats_get_writes(const struct dm_stats *dms,
- uint64_t region_id, uint64_t area_id);
-
-uint64_t dm_stats_get_writes_merged(const struct dm_stats *dms,
- uint64_t region_id, uint64_t area_id);
-
-uint64_t dm_stats_get_write_sectors(const struct dm_stats *dms,
- uint64_t region_id, uint64_t area_id);
-
-uint64_t dm_stats_get_write_nsecs(const struct dm_stats *dms,
- uint64_t region_id, uint64_t area_id);
-
-uint64_t dm_stats_get_io_in_progress(const struct dm_stats *dms,
- uint64_t region_id, uint64_t area_id);
-
-uint64_t dm_stats_get_io_nsecs(const struct dm_stats *dms,
- uint64_t region_id, uint64_t area_id);
-
-uint64_t dm_stats_get_weighted_io_nsecs(const struct dm_stats *dms,
- uint64_t region_id, uint64_t area_id);
-
-uint64_t dm_stats_get_total_read_nsecs(const struct dm_stats *dms,
- uint64_t region_id, uint64_t area_id);
-
-uint64_t dm_stats_get_total_write_nsecs(const 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
- */
-
-typedef enum {
- DM_STATS_RD_MERGES_PER_SEC,
- DM_STATS_WR_MERGES_PER_SEC,
- DM_STATS_READS_PER_SEC,
- DM_STATS_WRITES_PER_SEC,
- DM_STATS_READ_SECTORS_PER_SEC,
- DM_STATS_WRITE_SECTORS_PER_SEC,
- DM_STATS_AVERAGE_REQUEST_SIZE,
- DM_STATS_AVERAGE_QUEUE_SIZE,
- DM_STATS_AVERAGE_WAIT_TIME,
- DM_STATS_AVERAGE_RD_WAIT_TIME,
- DM_STATS_AVERAGE_WR_WAIT_TIME,
- DM_STATS_SERVICE_TIME,
- DM_STATS_THROUGHPUT,
- DM_STATS_UTILIZATION,
- DM_STATS_NR_METRICS
-} dm_stats_metric_t;
-
-int dm_stats_get_metric(const struct dm_stats *dms, int metric,
- uint64_t region_id, uint64_t area_id, double *value);
-
-int dm_stats_get_rd_merges_per_sec(const struct dm_stats *dms, double *rrqm,
- uint64_t region_id, uint64_t area_id);
-
-int dm_stats_get_wr_merges_per_sec(const struct dm_stats *dms, double *rrqm,
- uint64_t region_id, uint64_t area_id);
-
-int dm_stats_get_reads_per_sec(const struct dm_stats *dms, double *rd_s,
- uint64_t region_id, uint64_t area_id);
-
-int dm_stats_get_writes_per_sec(const struct dm_stats *dms, double *wr_s,
- uint64_t region_id, uint64_t area_id);
-
-int dm_stats_get_read_sectors_per_sec(const struct dm_stats *dms,
- double *rsec_s, uint64_t region_id,
- uint64_t area_id);
-
-int dm_stats_get_write_sectors_per_sec(const struct dm_stats *dms,
- double *wr_s, uint64_t region_id,
- uint64_t area_id);
-
-int dm_stats_get_average_request_size(const struct dm_stats *dms,
- double *arqsz, uint64_t region_id,
- uint64_t area_id);
-
-int dm_stats_get_service_time(const struct dm_stats *dms, double *svctm,
- uint64_t region_id, uint64_t area_id);
-
-int dm_stats_get_average_queue_size(const struct dm_stats *dms, double *qusz,
- uint64_t region_id, uint64_t area_id);
-
-int dm_stats_get_average_wait_time(const struct dm_stats *dms, double *await,
- uint64_t region_id, uint64_t area_id);
-
-int dm_stats_get_average_rd_wait_time(const struct dm_stats *dms,
- double *await, uint64_t region_id,
- uint64_t area_id);
-
-int dm_stats_get_average_wr_wait_time(const struct dm_stats *dms,
- double *await, uint64_t region_id,
- uint64_t area_id);
-
-int dm_stats_get_throughput(const struct dm_stats *dms, double *tput,
- uint64_t region_id, uint64_t area_id);
-
-int dm_stats_get_utilization(const struct dm_stats *dms, dm_percent_t *util,
- uint64_t region_id, uint64_t area_id);
-
-/*
- * Statistics histogram access methods.
- *
- * Methods to access latency histograms for regions that have them
- * enabled. Each histogram contains a configurable number of bins
- * spanning a user defined latency interval.
- *
- * The bin count, upper and lower bin bounds, and bin values are
- * made available via the following area methods.
- *
- * Methods to obtain a simple string representation of the histogram
- * and its bounds are also provided.
- */
-
-/*
- * Retrieve a pointer to the histogram associated with the specified
- * area. If the area does not have a histogram configured this function
- * returns NULL.
- *
- * The pointer does not need to be freed explicitly by the caller: it
- * will become invalid following a subsequent dm_stats_list(),
- * dm_stats_populate() or dm_stats_destroy() of the corresponding
- * dm_stats handle.
- *
- * If region_id or area_id is one of the special values
- * DM_STATS_REGION_CURRENT or DM_STATS_AREA_CURRENT the current cursor
- * value is used to select the region or area.
- */
-struct dm_histogram *dm_stats_get_histogram(const struct dm_stats *dms,
- uint64_t region_id,
- uint64_t area_id);
-
-/*
- * Return the number of bins in the specified histogram handle.
- */
-int dm_histogram_get_nr_bins(const struct dm_histogram *dmh);
-
-/*
- * Get the lower bound of the specified bin of the histogram for the
- * area specified by region_id and area_id. The value is returned in
- * nanoseconds.
- */
-uint64_t dm_histogram_get_bin_lower(const struct dm_histogram *dmh, int bin);
-
-/*
- * Get the upper bound of the specified bin of the histogram for the
- * area specified by region_id and area_id. The value is returned in
- * nanoseconds.
- */
-uint64_t dm_histogram_get_bin_upper(const struct dm_histogram *dmh, int bin);
-
-/*
- * Get the width of the specified bin of the histogram for the area
- * specified by region_id and area_id. The width is equal to the bin
- * upper bound minus the lower bound and yields the range of latency
- * values covered by this bin. The value is returned in nanoseconds.
- */
-uint64_t dm_histogram_get_bin_width(const struct dm_histogram *dmh, int bin);
-
-/*
- * Get the value of the specified bin of the histogram for the area
- * specified by region_id and area_id.
- */
-uint64_t dm_histogram_get_bin_count(const struct dm_histogram *dmh, int bin);
-
-/*
- * Get the percentage (relative frequency) of the specified bin of the
- * histogram for the area specified by region_id and area_id.
- */
-dm_percent_t dm_histogram_get_bin_percent(const struct dm_histogram *dmh,
- int bin);
-
-/*
- * Return the total observations (sum of bin counts) for the histogram
- * of the area specified by region_id and area_id.
- */
-uint64_t dm_histogram_get_sum(const struct dm_histogram *dmh);
-
-/*
- * Histogram formatting flags.
- */
-#define DM_HISTOGRAM_SUFFIX 0x1
-#define DM_HISTOGRAM_VALUES 0x2
-#define DM_HISTOGRAM_PERCENT 0X4
-#define DM_HISTOGRAM_BOUNDS_LOWER 0x10
-#define DM_HISTOGRAM_BOUNDS_UPPER 0x20
-#define DM_HISTOGRAM_BOUNDS_RANGE 0x30
-
-/*
- * Return a string representation of the supplied histogram's values and
- * bin boundaries.
- *
- * The bin argument selects the bin to format. If this argument is less
- * than zero all bins will be included in the resulting string.
- *
- * width specifies a minimum width for the field in characters; if it is
- * zero the width will be determined automatically based on the options
- * selected for formatting. A value less than zero disables field width
- * control: bin boundaries and values will be output with a minimum
- * amount of whitespace.
- *
- * flags is a collection of flag arguments that control the string format:
- *
- * DM_HISTOGRAM_VALUES - Include bin values in the string.
- * DM_HISTOGRAM_SUFFIX - Include time unit suffixes when printing bounds.
- * DM_HISTOGRAM_PERCENT - Format bin values as a percentage.
- *
- * DM_HISTOGRAM_BOUNDS_LOWER - Include the lower bound of each bin.
- * DM_HISTOGRAM_BOUNDS_UPPER - Include the upper bound of each bin.
- * DM_HISTOGRAM_BOUNDS_RANGE - Show the span of each bin as "lo-up".
- *
- * The returned pointer does not need to be freed explicitly by the
- * caller: it will become invalid following a subsequent
- * dm_stats_list(), dm_stats_populate() or dm_stats_destroy() of the
- * corresponding dm_stats handle.
- */
-const char *dm_histogram_to_string(const struct dm_histogram *dmh, int bin,
- int width, int flags);
-
/*************************
* config file parse/print
*************************/
diff --git a/device_mapper/libdm-stats.c b/device_mapper/libdm-stats.c
deleted file mode 100644
index 219d393..0000000
--- a/device_mapper/libdm-stats.c
+++ /dev/null
@@ -1,5096 +0,0 @@
-/*
- * Copyright (C) 2016 Red Hat, Inc. All rights reserved.
- *
- * _stats_get_extents_for_file() based in part on filefrag_fiemap() from
- * e2fsprogs/misc/filefrag.c. Copyright 2003 by Theodore Ts'o.
- *
- * This file is part of the device-mapper userspace tools.
- *
- * This copyrighted material is made available to anyone wishing to use,
- * modify, copy, or redistribute it subject to the terms and conditions
- * of the GNU Lesser General Public License v.2.1.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include "base/memory/zalloc.h"
-#include "misc/dmlib.h"
-#include "misc/kdev_t.h"
-
-#include "math.h" /* log10() */
-
-#include <sys/sysmacros.h>
-#include <sys/ioctl.h>
-#include <sys/vfs.h> /* fstatfs */
-#include <unistd.h>
-
-#ifdef __linux__
- #include <linux/fs.h> /* FS_IOC_FIEMAP */
-#endif
-
-#ifdef HAVE_LINUX_FIEMAP_H
- #include <linux/fiemap.h> /* fiemap */
-#endif
-
-#ifdef HAVE_LINUX_MAGIC_H
- #include <linux/magic.h> /* BTRFS_SUPER_MAGIC */
-#endif
-
-#define DM_STATS_REGION_NOT_PRESENT UINT64_MAX
-#define DM_STATS_GROUP_NOT_PRESENT DM_STATS_GROUP_NONE
-
-#define NSEC_PER_USEC 1000L
-#define NSEC_PER_MSEC 1000000L
-#define NSEC_PER_SEC 1000000000L
-
-#define PRECISE_ARG "precise_timestamps"
-#define HISTOGRAM_ARG "histogram:"
-
-#define STATS_ROW_BUF_LEN 4096
-#define STATS_MSG_BUF_LEN 1024
-#define STATS_FIE_BUF_LEN 2048
-
-#define SECTOR_SHIFT 9L
-
-/* Histogram bin */
-struct dm_histogram_bin {
- uint64_t upper; /* Upper bound on this bin. */
- uint64_t count; /* Count value for this bin. */
-};
-
-struct dm_histogram {
- /* The stats handle this histogram belongs to. */
- const struct dm_stats *dms;
- /* The region this histogram belongs to. */
- const struct dm_stats_region *region;
- uint64_t sum; /* Sum of histogram bin counts. */
- int nr_bins; /* Number of histogram bins assigned. */
- struct dm_histogram_bin bins[0];
-};
-
-/*
- * See Documentation/device-mapper/statistics.txt for full descriptions
- * of the device-mapper statistics counter fields.
- */
-struct dm_stats_counters {
- uint64_t reads; /* Num reads completed */
- uint64_t reads_merged; /* Num reads merged */
- uint64_t read_sectors; /* Num sectors read */
- uint64_t read_nsecs; /* Num milliseconds spent reading */
- uint64_t writes; /* Num writes completed */
- uint64_t writes_merged; /* Num writes merged */
- uint64_t write_sectors; /* Num sectors written */
- uint64_t write_nsecs; /* Num milliseconds spent writing */
- uint64_t io_in_progress; /* Num I/Os currently in progress */
- uint64_t io_nsecs; /* Num milliseconds spent doing I/Os */
- uint64_t weighted_io_nsecs; /* Weighted num milliseconds doing I/Os */
- uint64_t total_read_nsecs; /* Total time spent reading in milliseconds */
- uint64_t total_write_nsecs; /* Total time spent writing in milliseconds */
- struct dm_histogram *histogram; /* Histogram. */
-};
-
-struct dm_stats_region {
- uint64_t region_id; /* as returned by @stats_list */
- uint64_t group_id;
- uint64_t start;
- uint64_t len;
- uint64_t step;
- char *program_id;
- char *aux_data;
- uint64_t timescale; /* precise_timestamps is per-region */
- struct dm_histogram *bounds; /* histogram configuration */
- struct dm_histogram *histogram; /* aggregate cache */
- struct dm_stats_counters *counters;
-};
-
-struct dm_stats_group {
- uint64_t group_id;
- const char *alias;
- dm_bitset_t regions;
- struct dm_histogram *histogram;
-};
-
-struct dm_stats {
- /* device binding */
- int bind_major; /* device major that this dm_stats object is bound to */
- int bind_minor; /* device minor that this dm_stats object is bound to */
- char *bind_name; /* device-mapper device name */
- char *bind_uuid; /* device-mapper UUID */
- char *program_id; /* default program_id for this handle */
- const char *name; /* cached device_name used for reporting */
- struct dm_pool *mem; /* memory pool for region and counter tables */
- struct dm_pool *hist_mem; /* separate pool for histogram tables */
- struct dm_pool *group_mem; /* separate pool for group tables */
- uint64_t nr_regions; /* total number of present regions */
- uint64_t max_region; /* size of the regions table */
- uint64_t interval_ns; /* sampling interval in nanoseconds */
- uint64_t timescale; /* default sample value multiplier */
- int precise; /* use precise_timestamps when creating regions */
- struct dm_stats_region *regions;
- struct dm_stats_group *groups;
- /* statistics cursor */
- uint64_t walk_flags; /* walk control flags */
- uint64_t cur_flags;
- uint64_t cur_group;
- uint64_t cur_region;
- uint64_t cur_area;
-};
-
-#define PROC_SELF_COMM "/proc/self/comm"
-static char *_program_id_from_proc(void)
-{
- FILE *comm = NULL;
- char buf[STATS_ROW_BUF_LEN];
-
- if (!(comm = fopen(PROC_SELF_COMM, "r")))
- return_NULL;
-
- if (!fgets(buf, sizeof(buf), comm)) {
- log_error("Could not read from %s", PROC_SELF_COMM);
- if (fclose(comm))
- stack;
- return NULL;
- }
-
- if (fclose(comm))
- stack;
-
- return strdup(buf);
-}
-
-static uint64_t _nr_areas(uint64_t len, uint64_t step)
-{
- /* Default is one area. */
- if (!len || !step)
- return 1;
- /*
- * drivers/md/dm-stats.c::message_stats_create()
- * A region may be sub-divided into areas with their own counters.
- * Any partial area at the end of the region is treated as an
- * additional complete area.
- */
- return (len + step - 1) / step;
-}
-
-static uint64_t _nr_areas_region(struct dm_stats_region *region)
-{
- return _nr_areas(region->len, region->step);
-}
-
-struct dm_stats *dm_stats_create(const char *program_id)
-{
- size_t hist_hint = sizeof(struct dm_histogram_bin);
- size_t group_hint = sizeof(struct dm_stats_group);
- struct dm_stats *dms = NULL;
-
- if (!(dms = zalloc(sizeof(*dms))))
- return_NULL;
-
- /* FIXME: better hint. */
- if (!(dms->mem = dm_pool_create("stats_pool", 4096))) {
- free(dms);
- return_NULL;
- }
-
- if (!(dms->hist_mem = dm_pool_create("histogram_pool", hist_hint)))
- goto_bad;
-
- if (!(dms->group_mem = dm_pool_create("group_pool", group_hint)))
- goto_bad;
-
- if (!program_id || !strlen(program_id))
- dms->program_id = _program_id_from_proc();
- else
- dms->program_id = strdup(program_id);
-
- if (!dms->program_id) {
- log_error("Could not allocate memory for program_id");
- goto bad;
- }
-
- dms->bind_major = -1;
- dms->bind_minor = -1;
- dms->bind_name = NULL;
- dms->bind_uuid = NULL;
-
- dms->name = NULL;
-
- /* by default all regions use msec precision */
- dms->timescale = NSEC_PER_MSEC;
- dms->precise = 0;
-
- dms->nr_regions = DM_STATS_REGION_NOT_PRESENT;
- dms->max_region = DM_STATS_REGION_NOT_PRESENT;
- dms->regions = NULL;
-
- /* maintain compatibility with earlier walk version */
- dms->walk_flags = dms->cur_flags = DM_STATS_WALK_DEFAULT;
-
- return dms;
-
-bad:
- dm_pool_destroy(dms->mem);
- if (dms->hist_mem)
- dm_pool_destroy(dms->hist_mem);
- if (dms->group_mem)
- dm_pool_destroy(dms->group_mem);
- free(dms);
- return NULL;
-}
-
-/*
- * Test whether the stats region pointed to by region is present.
- */
-static int _stats_region_present(const struct dm_stats_region *region)
-{
- return !(region->region_id == DM_STATS_REGION_NOT_PRESENT);
-}
-
-/*
- * Test whether the stats group pointed to by group is present.
- */
-static int _stats_group_present(const struct dm_stats_group *group)
-{
- return !(group->group_id == DM_STATS_GROUP_NOT_PRESENT);
-}
-
-/*
- * Test whether a stats group id is present.
- */
-static int _stats_group_id_present(const struct dm_stats *dms, uint64_t id)
-{
- struct dm_stats_group *group = NULL;
-
- if (id == DM_STATS_GROUP_NOT_PRESENT)
- return 0;
-
- if (!dms)
- return_0;
-
- if (!dms->regions)
- return 0;
-
- if (id > dms->max_region)
- return 0;
-
- group = &dms->groups[id];
-
- return _stats_group_present(group);
-}
-
-/*
- * Test whether the given region_id is a member of any group.
- */
-static uint64_t _stats_region_is_grouped(const struct dm_stats* dms,
- uint64_t region_id)
-{
- uint64_t group_id;
-
- if (region_id == DM_STATS_GROUP_NOT_PRESENT)
- return 0;
-
- if (!_stats_region_present(&dms->regions[region_id]))
- return 0;
-
- group_id = dms->regions[region_id].group_id;
-
- return group_id != DM_STATS_GROUP_NOT_PRESENT;
-}
-
-static void _stats_histograms_destroy(struct dm_pool *mem,
- struct dm_stats_region *region)
-{
- /* Unpopulated handle. */
- if (!region->counters)
- return;
-
- /*
- * Free everything in the pool back to the first histogram.
- */
- if (region->counters[0].histogram)
- dm_pool_free(mem, region->counters[0].histogram);
-}
-
-static void _stats_region_destroy(struct dm_stats_region *region)
-{
- if (!_stats_region_present(region))
- return;
-
- region->start = region->len = region->step = 0;
- region->timescale = 0;
-
- /*
- * Don't free counters and histogram bounds here: they are
- * dropped from the pool along with the corresponding
- * regions table.
- *
- * The following objects are all allocated with malloc.
- */
-
- region->counters = NULL;
- region->bounds = NULL;
-
- free(region->program_id);
- region->program_id = NULL;
- free(region->aux_data);
- region->aux_data = NULL;
- region->region_id = DM_STATS_REGION_NOT_PRESENT;
-}
-
-static void _stats_regions_destroy(struct dm_stats *dms)
-{
- struct dm_pool *mem = dms->mem;
- uint64_t i;
-
- if (!dms->regions)
- return;
-
- /* walk backwards to obey pool order */
- for (i = dms->max_region; (i != DM_STATS_REGION_NOT_PRESENT); i--) {
- _stats_histograms_destroy(dms->hist_mem, &dms->regions[i]);
- _stats_region_destroy(&dms->regions[i]);
- }
-
- dm_pool_free(mem, dms->regions);
- dms->regions = NULL;
-}
-
-static void _stats_group_destroy(struct dm_stats_group *group)
-{
- if (!_stats_group_present(group))
- return;
-
- group->histogram = NULL;
-
- if (group->alias) {
- free((char *) group->alias);
- group->alias = NULL;
- }
- if (group->regions) {
- dm_bitset_destroy(group->regions);
- group->regions = NULL;
- }
- group->group_id = DM_STATS_GROUP_NOT_PRESENT;
-}
-
-static void _stats_groups_destroy(struct dm_stats *dms)
-{
- uint64_t i;
-
- if (!dms->groups)
- return;
-
- for (i = dms->max_region; (i != DM_STATS_REGION_NOT_PRESENT); i--)
- _stats_group_destroy(&dms->groups[i]);
- dm_pool_free(dms->group_mem, dms->groups);
- dms->groups = NULL;
-}
-
-static int _set_stats_device(struct dm_stats *dms, struct dm_task *dmt)
-{
- if (dms->bind_name)
- return dm_task_set_name(dmt, dms->bind_name);
- if (dms->bind_uuid)
- return dm_task_set_uuid(dmt, dms->bind_uuid);
- if (dms->bind_major > 0)
- return dm_task_set_major(dmt, dms->bind_major)
- && dm_task_set_minor(dmt, dms->bind_minor);
- return_0;
-}
-
-static int _stats_bound(const struct dm_stats *dms)
-{
- if (dms->bind_major > 0 || dms->bind_name || dms->bind_uuid)
- return 1;
- /* %p format specifier expects a void pointer. */
- log_error("Stats handle at %p is not bound.", dms);
- return 0;
-}
-
-static void _stats_clear_binding(struct dm_stats *dms)
-{
- if (dms->bind_name)
- dm_pool_free(dms->mem, dms->bind_name);
- if (dms->bind_uuid)
- dm_pool_free(dms->mem, dms->bind_uuid);
- free((char *) dms->name);
-
- dms->bind_name = dms->bind_uuid = NULL;
- dms->bind_major = dms->bind_minor = -1;
- dms->name = NULL;
-}
-
-int dm_stats_bind_devno(struct dm_stats *dms, int major, int minor)
-{
- _stats_clear_binding(dms);
- _stats_regions_destroy(dms);
- _stats_groups_destroy(dms);
-
- dms->bind_major = major;
- dms->bind_minor = minor;
-
- return 1;
-}
-
-int dm_stats_bind_name(struct dm_stats *dms, const char *name)
-{
- _stats_clear_binding(dms);
- _stats_regions_destroy(dms);
- _stats_groups_destroy(dms);
-
- if (!(dms->bind_name = dm_pool_strdup(dms->mem, name)))
- return_0;
-
- return 1;
-}
-
-int dm_stats_bind_uuid(struct dm_stats *dms, const char *uuid)
-{
- _stats_clear_binding(dms);
- _stats_regions_destroy(dms);
- _stats_groups_destroy(dms);
-
- if (!(dms->bind_uuid = dm_pool_strdup(dms->mem, uuid)))
- return_0;
-
- return 1;
-}
-
-int dm_stats_bind_from_fd(struct dm_stats *dms, int fd)
-{
- int major, minor;
- struct stat buf;
-
- if (fstat(fd, &buf)) {
- log_error("fstat failed for fd %d.", fd);
- return 0;
- }
-
- major = (int) MAJOR(buf.st_dev);
- minor = (int) MINOR(buf.st_dev);
-
- if (!dm_stats_bind_devno(dms, major, minor))
- return_0;
- return 1;
-}
-
-static int _stats_check_precise_timestamps(const struct dm_stats *dms)
-{
- /* Already checked? */
- if (dms && dms->precise)
- return 1;
-
- return dm_message_supports_precise_timestamps();
-}
-
-int dm_stats_driver_supports_precise(void)
-{
- return _stats_check_precise_timestamps(NULL);
-}
-
-int dm_stats_driver_supports_histogram(void)
-{
- return _stats_check_precise_timestamps(NULL);
-}
-
-static int _fill_hist_arg(char *hist_arg, size_t hist_len, uint64_t scale,
- struct dm_histogram *bounds)
-{
- int i, l, len = 0, nr_bins;
- char *arg = hist_arg;
- uint64_t value;
-
- nr_bins = bounds->nr_bins;
-
- for (i = 0; i < nr_bins; i++) {
- value = bounds->bins[i].upper / scale;
- if ((l = dm_snprintf(arg, hist_len - len, FMTu64"%s", value,
- (i == (nr_bins - 1)) ? "" : ",")) < 0)
- return_0;
- len += l;
- arg += l;
- }
- return 1;
-}
-
-static void *_get_hist_arg(struct dm_histogram *bounds, uint64_t scale,
- size_t *len)
-{
- struct dm_histogram_bin *entry, *bins;
- size_t hist_len = 1; /* terminating '\0' */
- double value;
-
- entry = bins = bounds->bins;
-
- entry += bounds->nr_bins - 1;
- while(entry >= bins) {
- value = (double) (entry--)->upper;
- /* Use lround to avoid size_t -> double cast warning. */
- hist_len += 1 + (size_t) lround(log10(value / scale));
- if (entry != bins)
- hist_len++; /* ',' */
- }
-
- *len = hist_len;
-
- return zalloc(hist_len);
-}
-
-static char *_build_histogram_arg(struct dm_histogram *bounds, int *precise)
-{
- struct dm_histogram_bin *entry, *bins;
- size_t hist_len;
- char *hist_arg;
- uint64_t scale;
-
- entry = bins = bounds->bins;
-
- /* Empty histogram is invalid. */
- if (!bounds->nr_bins) {
- log_error("Cannot format empty histogram description.");
- return NULL;
- }
-
- /* Validate entries and set *precise if precision < 1ms. */
- entry += bounds->nr_bins - 1;
- while (entry >= bins) {
- if (entry != bins) {
- if (entry->upper < (entry - 1)->upper) {
- log_error("Histogram boundaries must be in "
- "order of increasing magnitude.");
- return 0;
- }
- }
-
- /*
- * Only enable precise_timestamps automatically if any
- * value in the histogram bounds uses precision < 1ms.
- */
- if (((entry--)->upper % NSEC_PER_MSEC) && !*precise)
- *precise = 1;
- }
-
- scale = (*precise) ? 1 : NSEC_PER_MSEC;
-
- /* Calculate hist_len and allocate a character buffer. */
- if (!(hist_arg = _get_hist_arg(bounds, scale, &hist_len))) {
- log_error("Could not allocate memory for histogram argument.");
- return 0;
- }
-
- /* Fill hist_arg with boundary strings. */
- if (!_fill_hist_arg(hist_arg, hist_len, scale, bounds))
- goto_bad;
-
- return hist_arg;
-
-bad:
- log_error("Could not build histogram arguments.");
- free(hist_arg);
-
- return NULL;
-}
-
-static struct dm_task *_stats_send_message(struct dm_stats *dms, char *msg)
-{
- struct dm_task *dmt;
-
- if (!(dmt = dm_task_create(DM_DEVICE_TARGET_MSG)))
- return_0;
-
- if (!_set_stats_device(dms, dmt))
- goto_bad;
-
- if (!dm_task_set_message(dmt, msg))
- goto_bad;
-
- if (!dm_task_run(dmt))
- goto_bad;
-
- return dmt;
-
-bad:
- dm_task_destroy(dmt);
- return NULL;
-}
-
-/*
- * Cache the dm device_name for the device bound to dms.
- */
-static int _stats_set_name_cache(struct dm_stats *dms)
-{
- struct dm_task *dmt;
-
- if (dms->name)
- return 1;
-
- if (!(dmt = dm_task_create(DM_DEVICE_INFO)))
- return_0;
-
- if (!_set_stats_device(dms, dmt))
- goto_bad;
-
- if (!dm_task_run(dmt))
- goto_bad;
-
- if (!(dms->name = strdup(dm_task_get_name(dmt))))
- goto_bad;
-
- dm_task_destroy(dmt);
-
- return 1;
-
-bad:
- log_error("Could not retrieve device-mapper name for device.");
- dm_task_destroy(dmt);
- return 0;
-}
-
-/*
- * update region group_id values
- */
-static void _stats_update_groups(struct dm_stats *dms)
-{
- struct dm_stats_group *group;
- uint64_t group_id, i;
-
- for (group_id = 0; group_id < dms->max_region + 1; group_id++) {
- if (!_stats_group_id_present(dms, group_id))
- continue;
-
- group = &dms->groups[group_id];
-
- for (i = dm_bit_get_first(group->regions);
- i != DM_STATS_GROUP_NOT_PRESENT;
- i = dm_bit_get_next(group->regions, i))
- dms->regions[i].group_id = group_id;
- }
-}
-
-static void _check_group_regions_present(struct dm_stats *dms,
- struct dm_stats_group *group)
-{
- dm_bitset_t regions = group->regions;
- int64_t i, group_id;
-
- group_id = i = dm_bit_get_first(regions);
-
- for (; i > 0; i = dm_bit_get_next(regions, i))
- if (!_stats_region_present(&dms->regions[i])) {
- log_warn("Group descriptor " FMTd64 " contains "
- "non-existent region_id " FMTd64 ".",
- group_id, i);
- dm_bit_clear(regions, i);
- }
-}
-
-/*
- * Parse a DMS_GROUP group descriptor embedded in a region's aux_data.
- *
- * DMS_GROUP="ALIAS:MEMBERS"
- *
- * ALIAS: group alias
- * MEMBERS: list of group member region ids.
- *
- */
-#define DMS_GROUP_TAG "DMS_GROUP="
-#define DMS_GROUP_TAG_LEN (sizeof(DMS_GROUP_TAG) - 1)
-#define DMS_GROUP_SEP ':'
-#define DMS_AUX_SEP "#"
-
-static int _parse_aux_data_group(struct dm_stats *dms,
- struct dm_stats_region *region,
- struct dm_stats_group *group)
-{
- char *alias, *c, *end;
- dm_bitset_t regions;
-
- memset(group, 0, sizeof(*group));
- group->group_id = DM_STATS_GROUP_NOT_PRESENT;
-
- /* find start of group tag */
- c = strstr(region->aux_data, DMS_GROUP_TAG);
- if (!c)
- return 1; /* no group is not an error */
-
- alias = c + strlen(DMS_GROUP_TAG);
-
- c = strchr(c, DMS_GROUP_SEP);
-
- if (!c) {
- log_error("Found malformed group tag while reading aux_data");
- return 0;
- }
-
- /* terminate alias and advance to members */
- *(c++) = '\0';
-
- log_debug("Read alias '%s' from aux_data", alias);
-
- if (!c) {
- log_error("Found malformed group descriptor while "
- "reading aux_data, expected '%c'", DMS_GROUP_SEP);
- return 0;
- }
-
- /* if user aux_data follows make sure we have a terminated
- * string to pass to dm_bitset_parse_list().
- */
- end = strstr(c, DMS_AUX_SEP);
- if (!end)
- end = c + strlen(c);
- *(end++) = '\0';
-
- if (!(regions = dm_bitset_parse_list(c, NULL, 0))) {
- log_error("Could not parse member list while "
- "reading group aux_data");
- return 0;
- }
-
- group->group_id = dm_bit_get_first(regions);
- if (group->group_id != region->region_id) {
- log_error("Found invalid group descriptor in region " FMTu64
- " aux_data.", region->region_id);
- group->group_id = DM_STATS_GROUP_NOT_PRESENT;
- goto bad;
- }
-
- group->regions = regions;
- group->alias = NULL;
- if (strlen(alias)) {
- group->alias = strdup(alias);
- if (!group->alias) {
- log_error("Could not allocate memory for group alias");
- goto bad;
- }
- }
-
- /* separate group tag from user aux_data */
- if ((strlen(end) > 1) || strncmp(end, "-", 1))
- c = strdup(end);
- else
- c = strdup("");
-
- if (!c) {
- log_error("Could not allocate memory for user aux_data");
- goto bad_alias;
- }
-
- free(region->aux_data);
- region->aux_data = c;
-
- log_debug("Found group_id " FMTu64 ": alias=\"%s\"", group->group_id,
- (group->alias) ? group->alias : "");
-
- return 1;
-
-bad_alias:
- free((char *) group->alias);
-bad:
- dm_bitset_destroy(regions);
- return 0;
-}
-
-/*
- * Parse a histogram specification returned by the kernel in a
- * @stats_list response.
- */
-static int _stats_parse_histogram_spec(struct dm_stats *dms,
- struct dm_stats_region *region,
- const char *histogram)
-{
- static const char _valid_chars[] = "0123456789,";
- uint64_t scale = region->timescale, this_val = 0;
- struct dm_pool *mem = dms->hist_mem;
- struct dm_histogram_bin cur;
- struct dm_histogram hist;
- int nr_bins = 1;
- const char *c, *v, *val_start;
- char *p, *endptr = NULL;
-
- /* Advance past "histogram:". */
- histogram = strchr(histogram, ':');
- if (!histogram) {
- log_error("Could not parse histogram description.");
- return 0;
- }
- histogram++;
-
- /* @stats_list rows are newline terminated. */
- if ((p = strchr(histogram, '\n')))
- *p = '\0';
-
- if (!dm_pool_begin_object(mem, sizeof(cur)))
- return_0;
-
- memset(&hist, 0, sizeof(hist));
-
- hist.nr_bins = 0; /* fix later */
- hist.region = region;
- hist.dms = dms;
-
- if (!dm_pool_grow_object(mem, &hist, sizeof(hist)))
- goto_bad;
-
- c = histogram;
- do {
- for (v = _valid_chars; *v; v++)
- if (*c == *v)
- break;
- if (!*v) {
- stack;
- goto badchar;
- }
-
- if (*c == ',') {
- log_error("Invalid histogram description: %s",
- histogram);
- goto bad;
- } else {
- val_start = c;
- endptr = NULL;
-
- errno = 0;
- this_val = strtoull(val_start, &endptr, 10);
- if (errno || !endptr) {
- log_error("Could not parse histogram boundary.");
- goto bad;
- }
-
- c = endptr; /* Advance to units, comma, or end. */
-
- if (*c == ',')
- c++;
- else if (*c || (*c == ' ')) { /* Expected ',' or NULL. */
- stack;
- goto badchar;
- }
-
- if (*c == ',')
- c++;
-
- cur.upper = scale * this_val;
- cur.count = 0;
-
- if (!dm_pool_grow_object(mem, &cur, sizeof(cur)))
- goto_bad;
-
- nr_bins++;
- }
- } while (*c && (*c != ' '));
-
- /* final upper bound. */
- cur.upper = UINT64_MAX;
- if (!dm_pool_grow_object(mem, &cur, sizeof(cur)))
- goto_bad;
-
- region->bounds = dm_pool_end_object(mem);
-
- if (!region->bounds)
- return_0;
-
- region->bounds->nr_bins = nr_bins;
-
- log_debug("Added region histogram spec with %d entries.", nr_bins);
- return 1;
-
-badchar:
- log_error("Invalid character in histogram: '%c' (0x%x)", *c, *c);
-bad:
- dm_pool_abandon_object(mem);
- return 0;
-}
-
-static int _stats_parse_list_region(struct dm_stats *dms,
- struct dm_stats_region *region, char *line)
-{
- char *p = NULL, string_data[STATS_ROW_BUF_LEN];
- char *program_id, *aux_data, *stats_args;
- char *empty_string = (char *) "";
- int r;
-
- memset(string_data, 0, sizeof(string_data));
-
- /*
- * Parse fixed fields, line format:
- *
- * <region_id>: <start_sector>+<length> <step> <string data>
- *
- * Maximum string data size is 4096 - 1 bytes.
- */
- r = sscanf(line, FMTu64 ": " FMTu64 "+" FMTu64 " " FMTu64 " %4095c",
- ®ion->region_id, ®ion->start, ®ion->len,
- ®ion->step, string_data);
-
- if (r != 5)
- return_0;
-
- /* program_id is guaranteed to be first. */
- program_id = string_data;
-
- /*
- * FIXME: support embedded '\ ' in string data:
- * s/strchr/_find_unescaped_space()/
- */
- if ((p = strchr(string_data, ' '))) {
- /* terminate program_id string. */
- *p = '\0';
- if (!strncmp(program_id, "-", 1))
- program_id = empty_string;
- aux_data = p + 1;
- if ((p = strchr(aux_data, ' '))) {
- /* terminate aux_data string. */
- *p = '\0';
- stats_args = p + 1;
- } else
- stats_args = empty_string;
-
- /* no aux_data? */
- if (!strncmp(aux_data, "-", 1))
- aux_data = empty_string;
- else
- /* remove trailing newline */
- aux_data[strlen(aux_data) - 1] = '\0';
- } else
- aux_data = stats_args = empty_string;
-
- if (strstr(stats_args, PRECISE_ARG))
- region->timescale = 1;
- else
- region->timescale = NSEC_PER_MSEC;
-
- if ((p = strstr(stats_args, HISTOGRAM_ARG))) {
- if (!_stats_parse_histogram_spec(dms, region, p))
- return_0;
- } else
- region->bounds = NULL;
-
- /* clear aggregate cache */
- region->histogram = NULL;
-
- region->group_id = DM_STATS_GROUP_NOT_PRESENT;
-
- if (!(region->program_id = strdup(program_id)))
- return_0;
- if (!(region->aux_data = strdup(aux_data))) {
- free(region->program_id);
- return_0;
- }
-
- region->counters = NULL;
- return 1;
-}
-
-static int _stats_parse_list(struct dm_stats *dms, const char *resp)
-{
- uint64_t max_region = 0, nr_regions = 0;
- struct dm_stats_region cur, fill;
- struct dm_stats_group cur_group;
- struct dm_pool *mem = dms->mem, *group_mem = dms->group_mem;
- char line[STATS_ROW_BUF_LEN];
- FILE *list_rows;
-
- if (!resp) {
- log_error("Could not parse NULL @stats_list response.");
- return 0;
- }
-
- _stats_regions_destroy(dms);
- _stats_groups_destroy(dms);
-
- /* no regions */
- if (!strlen(resp)) {
- dms->nr_regions = dms->max_region = 0;
- dms->regions = NULL;
- return 1;
- }
-
- /*
- * dm_task_get_message_response() returns a 'const char *' but
- * since fmemopen also permits "w" it expects a 'char *'.
- */
- if (!(list_rows = fmemopen((char *)resp, strlen(resp), "r")))
- return_0;
-
- /* begin region table */
- if (!dm_pool_begin_object(mem, 1024))
- goto_bad;
-
- /* begin group table */
- if (!dm_pool_begin_object(group_mem, 32))
- goto_bad;
-
- while(fgets(line, sizeof(line), list_rows)) {
-
- cur_group.group_id = DM_STATS_GROUP_NOT_PRESENT;
- cur_group.regions = NULL;
- cur_group.alias = NULL;
-
- if (!_stats_parse_list_region(dms, &cur, line))
- goto_bad;
-
- /* handle holes in the list of region_ids */
- if (cur.region_id > max_region) {
- memset(&fill, 0, sizeof(fill));
- memset(&cur_group, 0, sizeof(cur_group));
- fill.region_id = DM_STATS_REGION_NOT_PRESENT;
- cur_group.group_id = DM_STATS_GROUP_NOT_PRESENT;
- do {
- if (!dm_pool_grow_object(mem, &fill, sizeof(fill)))
- goto_bad;
- if (!dm_pool_grow_object(group_mem, &cur_group,
- sizeof(cur_group)))
- goto_bad;
- } while (max_region++ < (cur.region_id - 1));
- }
-
- if (cur.aux_data)
- if (!_parse_aux_data_group(dms, &cur, &cur_group))
- log_error("Failed to parse group descriptor "
- "from region_id " FMTu64 " aux_data:"
- "'%s'", cur.region_id, cur.aux_data);
- /* continue */
-
- if (!dm_pool_grow_object(mem, &cur, sizeof(cur)))
- goto_bad;
-
- if (!dm_pool_grow_object(group_mem, &cur_group,
- sizeof(cur_group)))
- goto_bad;
-
- max_region++;
- nr_regions++;
- }
-
- if (!nr_regions)
- /* no region data read from @stats_list */
- goto bad;
-
- dms->nr_regions = nr_regions;
- dms->max_region = max_region - 1;
- dms->regions = dm_pool_end_object(mem);
- dms->groups = dm_pool_end_object(group_mem);
-
- dm_stats_foreach_group(dms)
- _check_group_regions_present(dms, &dms->groups[dms->cur_group]);
-
- _stats_update_groups(dms);
-
- if (fclose(list_rows))
- stack;
-
- return 1;
-
-bad:
- if (fclose(list_rows))
- stack;
- dm_pool_abandon_object(mem);
- dm_pool_abandon_object(group_mem);
-
- return 0;
-}
-
-int dm_stats_list(struct dm_stats *dms, const char *program_id)
-{
- char msg[STATS_MSG_BUF_LEN];
- struct dm_task *dmt;
- int r;
-
- if (!_stats_bound(dms))
- return_0;
-
- /* allow zero-length program_id for list */
- if (!program_id)
- program_id = dms->program_id;
-
- if (!_stats_set_name_cache(dms))
- return_0;
-
- if (dms->regions)
- _stats_regions_destroy(dms);
-
- r = dm_snprintf(msg, sizeof(msg), "@stats_list %s", program_id);
-
- if (r < 0) {
- log_error("Failed to prepare stats message.");
- return 0;
- }
-
- if (!(dmt = _stats_send_message(dms, msg)))
- return_0;
-
- if (!_stats_parse_list(dms, dm_task_get_message_response(dmt))) {
- log_error("Could not parse @stats_list response.");
- goto bad;
- }
-
- dm_task_destroy(dmt);
- return 1;
-
-bad:
- dm_task_destroy(dmt);
- return 0;
-}
-
-/*
- * Parse histogram data returned from a @stats_print operation.
- */
-static int _stats_parse_histogram(struct dm_pool *mem, char *hist_str,
- struct dm_histogram **histogram,
- struct dm_stats_region *region)
-{
- static const char _valid_chars[] = "0123456789:";
- struct dm_histogram *bounds = region->bounds;
- struct dm_histogram hist = {
- .nr_bins = region->bounds->nr_bins
- };
- const char *c, *v, *val_start;
- struct dm_histogram_bin cur;
- uint64_t sum = 0, this_val;
- char *endptr = NULL;
- int bin = 0;
-
- c = hist_str;
-
- if (!dm_pool_begin_object(mem, sizeof(cur)))
- return_0;
-
- if (!dm_pool_grow_object(mem, &hist, sizeof(hist)))
- goto_bad;
-
- do {
- memset(&cur, 0, sizeof(cur));
- for (v = _valid_chars; *v; v++)
- if (*c == *v)
- break;
- if (!*v)
- goto badchar;
-
- if (*c == ',')
- goto badchar;
- else {
- val_start = c;
- endptr = NULL;
-
- errno = 0;
- this_val = strtoull(val_start, &endptr, 10);
- if (errno || !endptr) {
- log_error("Could not parse histogram value.");
- goto bad;
- }
- c = endptr; /* Advance to colon, or end. */
-
- if (*c == ':')
- c++;
- else if (*c & (*c != '\n'))
- /* Expected ':', '\n', or NULL. */
- goto badchar;
-
- if (*c == ':')
- c++;
-
- cur.upper = bounds->bins[bin].upper;
- cur.count = this_val;
- sum += this_val;
-
- if (!dm_pool_grow_object(mem, &cur, sizeof(cur)))
- goto_bad;
-
- bin++;
- }
- } while (*c && (*c != '\n'));
-
- log_debug("Added region histogram data with %d entries.", hist.nr_bins);
-
- *histogram = dm_pool_end_object(mem);
- (*histogram)->sum = sum;
-
- return 1;
-
-badchar:
- log_error("Invalid character in histogram data: '%c' (0x%x)", *c, *c);
-bad:
- dm_pool_abandon_object(mem);
- return 0;
-}
-
-static int _stats_parse_region(struct dm_stats *dms, const char *resp,
- struct dm_stats_region *region,
- uint64_t timescale)
-{
- struct dm_histogram *hist = NULL;
- struct dm_pool *mem = dms->mem;
- struct dm_stats_counters cur;
- FILE *stats_rows = NULL;
- uint64_t start = 0, len = 0;
- char row[STATS_ROW_BUF_LEN];
- int r;
-
- if (!resp) {
- log_error("Could not parse empty @stats_print response.");
- return 0;
- }
-
- region->start = UINT64_MAX;
-
- if (!dm_pool_begin_object(mem, 512))
- goto_bad;
-
- /*
- * 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_bad;
-
- /*
- * Output format for each step-sized area of a region:
- *
- * <start_sector>+<length> counters
- *
- * The first 11 counters have the same meaning as
- * /sys/block/ * /stat or /proc/diskstats.
- *
- * Please refer to Documentation/iostats.txt for details.
- *
- * 1. the number of reads completed
- * 2. the number of reads merged
- * 3. the number of sectors read
- * 4. the number of milliseconds spent reading
- * 5. the number of writes completed
- * 6. the number of writes merged
- * 7. the number of sectors written
- * 8. the number of milliseconds spent writing
- * 9. the number of I/Os currently in progress
- * 10. the number of milliseconds spent doing I/Os
- * 11. the weighted number of milliseconds spent doing I/Os
- *
- * Additional counters:
- * 12. the total time spent reading in milliseconds
- * 13. the total time spent writing in milliseconds
- *
- */
- while (fgets(row, sizeof(row), stats_rows)) {
- r = sscanf(row, FMTu64 "+" FMTu64 /* start+len */
- /* reads */
- FMTu64 " " FMTu64 " " FMTu64 " " FMTu64 " "
- /* writes */
- FMTu64 " " FMTu64 " " FMTu64 " " FMTu64 " "
- /* in flight & io nsecs */
- FMTu64 " " FMTu64 " " FMTu64 " "
- /* tot read/write nsecs */
- FMTu64 " " FMTu64, &start, &len,
- &cur.reads, &cur.reads_merged, &cur.read_sectors,
- &cur.read_nsecs,
- &cur.writes, &cur.writes_merged, &cur.write_sectors,
- &cur.write_nsecs,
- &cur.io_in_progress,
- &cur.io_nsecs, &cur.weighted_io_nsecs,
- &cur.total_read_nsecs, &cur.total_write_nsecs);
- if (r != 15) {
- log_error("Could not parse @stats_print row.");
- goto bad;
- }
-
- /* scale time values up if needed */
- if (timescale != 1) {
- cur.read_nsecs *= timescale;
- cur.write_nsecs *= timescale;
- cur.io_nsecs *= timescale;
- cur.weighted_io_nsecs *= timescale;
- cur.total_read_nsecs *= timescale;
- cur.total_write_nsecs *= timescale;
- }
-
- if (region->bounds) {
- /* Find first histogram separator. */
- char *hist_str = strchr(row, ':');
- if (!hist_str) {
- log_error("Could not parse histogram value.");
- goto bad;
- }
- /* Find space preceding histogram. */
- while (hist_str && *(hist_str - 1) != ' ')
- hist_str--;
-
- /* Use a separate pool for histogram objects since we
- * are growing the area table and each area's histogram
- * table simultaneously.
- */
- if (!_stats_parse_histogram(dms->hist_mem, hist_str,
- &hist, region))
- goto_bad;
- hist->dms = dms;
- hist->region = region;
- }
-
- cur.histogram = hist;
-
- if (!dm_pool_grow_object(mem, &cur, sizeof(cur)))
- goto_bad;
-
- if (region->start == UINT64_MAX) {
- region->start = start;
- region->step = len; /* area size is always uniform. */
- }
- }
-
- if (region->start == UINT64_MAX)
- /* no area data read from @stats_print */
- goto bad;
-
- region->len = (start + len) - region->start;
- region->timescale = timescale;
- region->counters = dm_pool_end_object(mem);
-
- if (fclose(stats_rows))
- stack;
-
- return 1;
-
-bad:
- if (stats_rows)
- if (fclose(stats_rows))
- stack;
- dm_pool_abandon_object(mem);
-
- return 0;
-}
-
-static void _stats_walk_next_present(const struct dm_stats *dms,
- uint64_t *flags,
- uint64_t *cur_r, uint64_t *cur_a,
- uint64_t *cur_g)
-{
- struct dm_stats_region *cur = NULL;
-
- /* start of walk: region loop advances *cur_r to 0. */
- if (*cur_r != DM_STATS_REGION_NOT_PRESENT)
- cur = &dms->regions[*cur_r];
-
- /* within current region? */
- if (cur && (*flags & DM_STATS_WALK_AREA)) {
- if (++(*cur_a) < _nr_areas_region(cur))
- return;
- else
- *cur_a = 0;
- }
-
- /* advance to next present, non-skipped region or end */
- while (++(*cur_r) <= dms->max_region) {
- cur = &dms->regions[*cur_r];
- if (!_stats_region_present(cur))
- continue;
- if ((*flags & DM_STATS_WALK_SKIP_SINGLE_AREA))
- if (!(*flags & DM_STATS_WALK_AREA))
- if (_nr_areas_region(cur) < 2)
- continue;
- /* matching region found */
- break;
- }
- return;
-}
-
-static void _stats_walk_next(const struct dm_stats *dms, uint64_t *flags,
- uint64_t *cur_r, uint64_t *cur_a, uint64_t *cur_g)
-{
- if (!dms || !dms->regions)
- return;
-
- if (*flags & DM_STATS_WALK_AREA) {
- /* advance to next area, region, or end */
- _stats_walk_next_present(dms, flags, cur_r, cur_a, cur_g);
- return;
- }
-
- if (*flags & DM_STATS_WALK_REGION) {
- /* enable region aggregation */
- *cur_a = DM_STATS_WALK_REGION;
- _stats_walk_next_present(dms, flags, cur_r, cur_a, cur_g);
- return;
- }
-
- if (*flags & DM_STATS_WALK_GROUP) {
- /* enable group aggregation */
- *cur_r = *cur_a = DM_STATS_WALK_GROUP;
- while (!_stats_group_id_present(dms, ++(*cur_g))
- && (*cur_g) < dms->max_region + 1)
- ; /* advance to next present group or end */
- return;
- }
-
- log_error("stats_walk_next called with empty walk flags");
-}
-
-static void _group_walk_start(const struct dm_stats *dms, uint64_t *flags,
- uint64_t *cur_r, uint64_t *cur_a, uint64_t *cur_g)
-{
- if (!(*flags & DM_STATS_WALK_GROUP))
- return;
-
- *cur_a = *cur_r = DM_STATS_WALK_GROUP;
- *cur_g = 0;
-
- /* advance to next present group or end */
- while ((*cur_g) <= dms->max_region) {
- if (_stats_region_is_grouped(dms, *cur_g))
- break;
- (*cur_g)++;
- }
-
- if (*cur_g > dms->max_region)
- /* no groups to walk */
- *flags &= ~DM_STATS_WALK_GROUP;
-}
-
-static void _stats_walk_start(const struct dm_stats *dms, uint64_t *flags,
- uint64_t *cur_r, uint64_t *cur_a,
- uint64_t *cur_g)
-{
- log_debug("starting stats walk with %s %s %s %s",
- (*flags & DM_STATS_WALK_AREA) ? "AREA" : "",
- (*flags & DM_STATS_WALK_REGION) ? "REGION" : "",
- (*flags & DM_STATS_WALK_GROUP) ? "GROUP" : "",
- (*flags & DM_STATS_WALK_SKIP_SINGLE_AREA) ? "SKIP" : "");
-
- if (!dms->regions)
- return;
-
- if (!(*flags & (DM_STATS_WALK_AREA | DM_STATS_WALK_REGION)))
- return _group_walk_start(dms, flags, cur_r, cur_a, cur_g);
-
- /* initialise cursor state */
- *cur_a = 0;
- *cur_r = DM_STATS_REGION_NOT_PRESENT;
- *cur_g = DM_STATS_GROUP_NOT_PRESENT;
-
- if (!(*flags & DM_STATS_WALK_AREA))
- *cur_a = DM_STATS_WALK_REGION;
-
- /* advance to first present, non-skipped region */
- _stats_walk_next_present(dms, flags, cur_r, cur_a, cur_g);
-}
-
-#define DM_STATS_WALK_MASK (DM_STATS_WALK_AREA \
- | DM_STATS_WALK_REGION \
- | DM_STATS_WALK_GROUP \
- | DM_STATS_WALK_SKIP_SINGLE_AREA)
-
-int dm_stats_walk_init(struct dm_stats *dms, uint64_t flags)
-{
- if (!dms)
- return_0;
-
- if (flags & ~DM_STATS_WALK_MASK) {
- log_error("Unknown value in walk flags: 0x" FMTx64,
- (uint64_t) (flags & ~DM_STATS_WALK_MASK));
- return 0;
- }
- dms->walk_flags = flags;
- log_debug("dm_stats_walk_init: initialised flags to " FMTx64, flags);
- return 1;
-}
-
-void dm_stats_walk_start(struct dm_stats *dms)
-{
- if (!dms || !dms->regions)
- return;
-
- dms->cur_flags = dms->walk_flags;
-
- _stats_walk_start(dms, &dms->cur_flags,
- &dms->cur_region, &dms->cur_area,
- &dms->cur_group);
-}
-
-void dm_stats_walk_next(struct dm_stats *dms)
-{
- _stats_walk_next(dms, &dms->cur_flags,
- &dms->cur_region, &dms->cur_area,
- &dms->cur_group);
-}
-
-void dm_stats_walk_next_region(struct dm_stats *dms)
-{
- dms->cur_flags &= ~DM_STATS_WALK_AREA;
- _stats_walk_next(dms, &dms->cur_flags,
- &dms->cur_region, &dms->cur_area,
- &dms->cur_group);
-}
-
-/*
- * Return 1 if any regions remain that are present and not skipped
- * by the current walk flags or 0 otherwise.
- */
-static uint64_t _stats_walk_any_unskipped(const struct dm_stats *dms,
- uint64_t *flags,
- uint64_t *cur_r, uint64_t *cur_a)
-{
- struct dm_stats_region *region;
- uint64_t i;
-
- if (*cur_r > dms->max_region)
- return 0;
-
- for (i = *cur_r; i <= dms->max_region; i++) {
- region = &dms->regions[i];
- if (!_stats_region_present(region))
- continue;
- if ((*flags & DM_STATS_WALK_SKIP_SINGLE_AREA)
- && !(*flags & DM_STATS_WALK_AREA))
- if (_nr_areas_region(region) < 2)
- continue;
- return 1;
- }
- return 0;
-}
-
-static void _stats_walk_end_areas(const struct dm_stats *dms, uint64_t *flags,
- uint64_t *cur_r, uint64_t *cur_a,
- uint64_t *cur_g)
-{
- int end = !_stats_walk_any_unskipped(dms, flags, cur_r, cur_a);
-
- if (!(*flags & DM_STATS_WALK_AREA))
- return;
-
- if (!end)
- return;
-
- *flags &= ~DM_STATS_WALK_AREA;
- if (*flags & DM_STATS_WALK_REGION) {
- /* start region walk */
- *cur_a = DM_STATS_WALK_REGION;
- *cur_r = DM_STATS_REGION_NOT_PRESENT;
- _stats_walk_next_present(dms, flags, cur_r, cur_a, cur_g);
- if (!_stats_walk_any_unskipped(dms, flags, cur_r, cur_a)) {
- /* no more regions */
- *flags &= ~DM_STATS_WALK_REGION;
- if (!(*flags & DM_STATS_WALK_GROUP))
- *cur_r = dms->max_region;
- }
- }
-
- if (*flags & DM_STATS_WALK_REGION)
- return;
-
- if (*flags & DM_STATS_WALK_GROUP)
- _group_walk_start(dms, flags, cur_r, cur_a, cur_g);
-}
-
-static int _stats_walk_end(const struct dm_stats *dms, uint64_t *flags,
- uint64_t *cur_r, uint64_t *cur_a, uint64_t *cur_g)
-{
- if (*flags & DM_STATS_WALK_AREA) {
- _stats_walk_end_areas(dms, flags, cur_r, cur_a, cur_g);
- goto out;
- }
-
- if (*flags & DM_STATS_WALK_REGION) {
- if (!_stats_walk_any_unskipped(dms, flags, cur_r, cur_a)) {
- *flags &= ~DM_STATS_WALK_REGION;
- _group_walk_start(dms, flags, cur_r, cur_a, cur_g);
- }
- goto out;
- }
-
- if (*flags & DM_STATS_WALK_GROUP) {
- if (*cur_g <= dms->max_region)
- goto out;
- *flags &= ~DM_STATS_WALK_GROUP;
- }
-out:
- return !(*flags & ~DM_STATS_WALK_SKIP_SINGLE_AREA);
-}
-
-int dm_stats_walk_end(struct dm_stats *dms)
-{
- if (!dms)
- return 1;
-
- if (_stats_walk_end(dms, &dms->cur_flags,
- &dms->cur_region, &dms->cur_area,
- &dms->cur_group)) {
- dms->cur_flags = dms->walk_flags;
- return 1;
- }
- return 0;
-}
-
-dm_stats_obj_type_t dm_stats_object_type(const struct dm_stats *dms,
- uint64_t region_id,
- uint64_t area_id)
-{
- uint64_t group_id;
-
- region_id = (region_id == DM_STATS_REGION_CURRENT)
- ? dms->cur_region : region_id ;
- area_id = (area_id == DM_STATS_AREA_CURRENT)
- ? dms->cur_area : area_id ;
-
- if (region_id == DM_STATS_REGION_NOT_PRESENT)
- /* no region */
- return DM_STATS_OBJECT_TYPE_NONE;
-
- if (region_id & DM_STATS_WALK_GROUP) {
- if (region_id == DM_STATS_WALK_GROUP)
- /* indirect group_id from cursor */
- group_id = dms->cur_group;
- else
- /* immediate group_id encoded in region_id */
- group_id = region_id & ~DM_STATS_WALK_GROUP;
- if (!_stats_group_id_present(dms, group_id))
- return DM_STATS_OBJECT_TYPE_NONE;
- return DM_STATS_OBJECT_TYPE_GROUP;
- }
-
- if (region_id > dms->max_region)
- /* end of table */
- return DM_STATS_OBJECT_TYPE_NONE;
-
- if (area_id & DM_STATS_WALK_REGION)
- /* aggregate region */
- return DM_STATS_OBJECT_TYPE_REGION;
-
- /* plain region_id and area_id */
- return DM_STATS_OBJECT_TYPE_AREA;
-}
-
-dm_stats_obj_type_t dm_stats_current_object_type(const struct dm_stats *dms)
-{
- /* dm_stats_object_type will decode region/area */
- return dm_stats_object_type(dms,
- DM_STATS_REGION_CURRENT,
- DM_STATS_AREA_CURRENT);
-}
-
-uint64_t dm_stats_get_region_nr_areas(const struct dm_stats *dms,
- uint64_t region_id)
-{
- struct dm_stats_region *region = NULL;
-
- /* groups or aggregate regions cannot be subdivided */
- if (region_id & DM_STATS_WALK_GROUP)
- return 1;
-
- region = &dms->regions[region_id];
- return _nr_areas_region(region);
-}
-
-uint64_t dm_stats_get_current_nr_areas(const struct dm_stats *dms)
-{
- /* groups or aggregate regions cannot be subdivided */
- if (dms->cur_region & DM_STATS_WALK_GROUP)
- return 1;
-
- return dm_stats_get_region_nr_areas(dms, dms->cur_region);
-}
-
-uint64_t dm_stats_get_nr_areas(const struct dm_stats *dms)
-{
- uint64_t nr_areas = 0, flags = DM_STATS_WALK_AREA;
- /* use a separate cursor */
- uint64_t cur_region = 0, cur_area = 0, cur_group = 0;
-
- /* no regions to visit? */
- if (!dms->regions)
- return 0;
-
- flags = DM_STATS_WALK_AREA;
- _stats_walk_start(dms, &flags, &cur_region, &cur_area, &cur_group);
- do {
- nr_areas += dm_stats_get_current_nr_areas(dms);
- _stats_walk_next(dms, &flags,
- &cur_region, &cur_area,
- &cur_group);
- } while (!_stats_walk_end(dms, &flags,
- &cur_region, &cur_area,
- &cur_group));
- return nr_areas;
-}
-
-int dm_stats_group_present(const struct dm_stats *dms, uint64_t group_id)
-{
- return _stats_group_id_present(dms, group_id);
-}
-
-int dm_stats_get_region_nr_histogram_bins(const struct dm_stats *dms,
- uint64_t region_id)
-{
- region_id = (region_id == DM_STATS_REGION_CURRENT)
- ? dms->cur_region : region_id ;
-
- /* FIXME: support group histograms if all region bounds match */
- if (region_id & DM_STATS_WALK_GROUP)
- return 0;
-
- if (!dms->regions[region_id].bounds)
- return 0;
-
- return dms->regions[region_id].bounds->nr_bins;
-}
-
-/*
- * Fill buf with a list of set regions in the regions bitmap. Consecutive
- * ranges of set region IDs are output using "M-N" range notation.
- *
- * The number of bytes consumed is returned or zero on error.
- */
-static size_t _stats_group_tag_fill(const struct dm_stats *dms,
- dm_bitset_t regions,
- char *buf, size_t buflen)
-{
- int i, j, r, next, last = 0;
- size_t used = 0;
-
- last = dm_bit_get_last(regions);
-
- i = dm_bit_get_first(regions);
- for(; i >= 0; i = dm_bit_get_next(regions, i)) {
- /* find range end */
- j = i;
- do
- next = j + 1;
- while ((j = dm_bit_get_next(regions, j)) == next);
-
- /* set to last set bit */
- j = next - 1;
-
- /* handle range vs. single region */
- if (i != j)
- r = dm_snprintf(buf, buflen, FMTu64 "-" FMTu64 "%s",
- (uint64_t) i, (uint64_t) j,
- (j == last) ? "" : ",");
- else
- r = dm_snprintf(buf, buflen, FMTu64 "%s", (uint64_t) i,
- (i == last) ? "" : ",");
- if (r < 0)
- goto_bad;
-
- i = next; /* skip handled bits if in range */
-
- buf += r;
- used += r;
- }
-
- return used;
-bad:
- log_error("Could not format group list.");
- return 0;
-}
-
-/*
- * Calculate the space required to hold a string description of the group
- * described by the regions bitset using comma separated list in range
- * notation ("A,B,C,M-N").
- */
-static size_t _stats_group_tag_len(const struct dm_stats *dms,
- dm_bitset_t regions)
-{
- int64_t i, j, next, nr_regions = 0;
- size_t buflen = 0, id_len = 0;
-
- /* check region ids and find last set bit */
- i = dm_bit_get_first(regions);
- for (; i >= 0; i = dm_bit_get_next(regions, i)) {
- /* length of region_id or range start in characters */
- id_len = (i) ? 1 + (size_t) log10(i) : 1;
- buflen += id_len;
- j = i;
- do
- next = j + 1;
- while ((j = dm_bit_get_next(regions, j)) == next);
-
- /* set to last set bit */
- j = next - 1;
-
- nr_regions += j - i + 1;
-
- /* handle range */
- if (i != j) {
- /* j is always > i, which is always >= 0 */
- id_len = 1 + (size_t) log10(j);
- buflen += id_len + 1; /* range end plus "-" */
- }
- buflen++;
- i = next; /* skip bits if handling range */
- }
- return buflen;
-}
-
-/*
- * Build a DMS_GROUP="..." tag for the group specified by group_id,
- * to be stored in the corresponding region's aux_data field.
- */
-static char *_build_group_tag(struct dm_stats *dms, uint64_t group_id)
-{
- char *aux_string, *buf;
- dm_bitset_t regions;
- const char *alias;
- size_t buflen = 0;
- int r;
-
- regions = dms->groups[group_id].regions;
- alias = dms->groups[group_id].alias;
-
- buflen = _stats_group_tag_len(dms, regions);
-
- if (!buflen)
- return_0;
-
- buflen += DMS_GROUP_TAG_LEN;
- buflen += 1 + (alias ? strlen(alias) : 0); /* 'alias:' */
-
- buf = aux_string = malloc(buflen);
- if (!buf) {
- log_error("Could not allocate memory for aux_data string.");
- return NULL;
- }
-
- if (!dm_strncpy(buf, DMS_GROUP_TAG, DMS_GROUP_TAG_LEN + 1))
- goto_bad;
-
- buf += DMS_GROUP_TAG_LEN;
- buflen -= DMS_GROUP_TAG_LEN;
-
- r = dm_snprintf(buf, buflen, "%s%c", alias ? alias : "", DMS_GROUP_SEP);
- if (r < 0)
- goto_bad;
-
- buf += r;
- buflen -= r;
-
- r = _stats_group_tag_fill(dms, regions, buf, buflen);
- if (!r)
- goto_bad;
-
- return aux_string;
-bad:
- log_error("Could not format group aux_data.");
- free(aux_string);
- return NULL;
-}
-
-/*
- * Store updated aux_data for a region. The aux_data is passed to the
- * kernel using the @stats_set_aux message. Any required group tag is
- * generated from the current group table and included in the message.
- */
-static int _stats_set_aux(struct dm_stats *dms,
- uint64_t region_id, const char *aux_data)
-{
- const char *group_tag = NULL;
- struct dm_task *dmt = NULL;
- char msg[STATS_MSG_BUF_LEN];
-
- /* group data required? */
- if (_stats_group_id_present(dms, region_id)) {
- group_tag = _build_group_tag(dms, region_id);
- if (!group_tag) {
- log_error("Could not build group descriptor for "
- "region ID " FMTu64, region_id);
- goto bad;
- }
- }
-
- if (dm_snprintf(msg, sizeof(msg), "@stats_set_aux " FMTu64 " %s%s%s ",
- region_id, (group_tag) ? group_tag : "",
- (group_tag) ? DMS_AUX_SEP : "",
- (strlen(aux_data)) ? aux_data : "-") < 0) {
- log_error("Could not prepare @stats_set_aux message");
- goto bad;
- }
-
- if (!(dmt = _stats_send_message(dms, msg)))
- goto_bad;
-
- free((char *) group_tag);
-
- /* no response to a @stats_set_aux message */
- dm_task_destroy(dmt);
-
- return 1;
-bad:
- free((char *) group_tag);
- return 0;
-}
-
-/*
- * Maximum length of a "start+end" range string:
- * Two 20 digit uint64_t, '+', and NULL.
- */
-#define RANGE_LEN 42
-static int _stats_create_region(struct dm_stats *dms, uint64_t *region_id,
- uint64_t start, uint64_t len, int64_t step,
- int precise, const char *hist_arg,
- const char *program_id, const char *aux_data)
-{
- char msg[STATS_MSG_BUF_LEN], range[RANGE_LEN], *endptr = NULL;
- const char *err_fmt = "Could not prepare @stats_create %s.";
- const char *precise_str = PRECISE_ARG;
- const char *resp, *opt_args = NULL;
- struct dm_task *dmt = NULL;
- int r = 0, nr_opt = 0;
-
- if (!_stats_bound(dms))
- return_0;
-
- if (!program_id || !strlen(program_id))
- program_id = dms->program_id;
-
- if (start || len) {
- if (dm_snprintf(range, sizeof(range), FMTu64 "+" FMTu64,
- start, len) < 0) {
- log_error(err_fmt, "range");
- return 0;
- }
- }
-
- if (precise < 0)
- precise = dms->precise;
-
- if (precise)
- nr_opt++;
- else
- precise_str = "";
-
- if (hist_arg)
- nr_opt++;
- else
- hist_arg = "";
-
- if (nr_opt) {
- if ((dm_asprintf((char **)&opt_args, "%d %s %s%s", nr_opt,
- precise_str,
- (strlen(hist_arg)) ? HISTOGRAM_ARG : "",
- hist_arg)) < 0) {
- log_error(err_fmt, PRECISE_ARG " option.");
- return 0;
- }
- } else
- opt_args = strdup("");
-
- if (dm_snprintf(msg, sizeof(msg), "@stats_create %s %s" FMTu64
- " %s %s %s", (start || len) ? range : "-",
- (step < 0) ? "/" : "",
- (uint64_t)llabs(step),
- opt_args, program_id, aux_data) < 0) {
- log_error(err_fmt, "message");
- free((void *) opt_args);
- return 0;
- }
-
- if (!(dmt = _stats_send_message(dms, msg)))
- goto_out;
-
- resp = dm_task_get_message_response(dmt);
- if (!resp) {
- log_error("Could not parse empty @stats_create response.");
- goto out;
- }
-
- if (region_id) {
- errno = 0;
- *region_id = strtoull(resp, &endptr, 10);
- if (errno || resp == endptr)
- goto_out;
- }
-
- r = 1;
-
-out:
- if (dmt)
- dm_task_destroy(dmt);
- free((void *) opt_args);
-
- return r;
-}
-
-int dm_stats_create_region(struct dm_stats *dms, uint64_t *region_id,
- uint64_t start, uint64_t len, int64_t step,
- int precise, struct dm_histogram *bounds,
- const char *program_id, const char *user_data)
-{
- char *hist_arg = NULL;
- int r = 0;
-
- /* Nanosecond counters and histograms both need precise_timestamps. */
- if ((precise || bounds) && !_stats_check_precise_timestamps(dms))
- return_0;
-
- if (bounds) {
- /* _build_histogram_arg enables precise if vals < 1ms. */
- if (!(hist_arg = _build_histogram_arg(bounds, &precise)))
- goto_out;
- }
-
- r = _stats_create_region(dms, region_id, start, len, step,
- precise, hist_arg, program_id, user_data);
- free(hist_arg);
-
-out:
- return r;
-}
-
-static void _stats_clear_group_regions(struct dm_stats *dms, uint64_t group_id)
-{
- struct dm_stats_group *group;
- uint64_t i;
-
- group = &dms->groups[group_id];
- for (i = dm_bit_get_first(group->regions);
- i != DM_STATS_GROUP_NOT_PRESENT;
- i = dm_bit_get_next(group->regions, i))
- dms->regions[i].group_id = DM_STATS_GROUP_NOT_PRESENT;
-}
-
-static int _stats_remove_region_id_from_group(struct dm_stats *dms,
- uint64_t region_id)
-{
- struct dm_stats_region *region = &dms->regions[region_id];
- uint64_t group_id = region->group_id;
- dm_bitset_t regions = dms->groups[group_id].regions;
-
- if (!_stats_region_is_grouped(dms, region_id))
- return_0;
-
- dm_bit_clear(regions, region_id);
-
- /* removing group leader? */
- if (region_id == group_id) {
- _stats_clear_group_regions(dms, group_id);
- _stats_group_destroy(&dms->groups[group_id]);
- }
-
- return _stats_set_aux(dms, group_id, dms->regions[group_id].aux_data);
-}
-
-static int _stats_delete_region(struct dm_stats *dms, uint64_t region_id)
-{
- char msg[STATS_MSG_BUF_LEN];
- struct dm_task *dmt;
-
- if (_stats_region_is_grouped(dms, region_id))
- if (!_stats_remove_region_id_from_group(dms, region_id)) {
- log_error("Could not remove region ID " FMTu64 " from "
- "group ID " FMTu64,
- region_id, dms->regions[region_id].group_id);
- return 0;
- }
-
- if (dm_snprintf(msg, sizeof(msg), "@stats_delete " FMTu64, region_id) < 0) {
- log_error("Could not prepare @stats_delete message.");
- return 0;
- }
-
- dmt = _stats_send_message(dms, msg);
- if (!dmt)
- return_0;
- dm_task_destroy(dmt);
-
- return 1;
-}
-
-int dm_stats_delete_region(struct dm_stats *dms, uint64_t region_id)
-{
- int listed = 0;
-
- if (!_stats_bound(dms))
- return_0;
-
- /*
- * To correctly delete a region, that may be part of a group, a
- * listed handle is required, since the region may need to be
- * removed from another region's group descriptor; earlier
- * versions of the region deletion interface do not have this
- * requirement since there are no dependencies between regions.
- *
- * Listing a previously unlisted handle has numerous
- * side-effects on other calls and operations (e.g. stats
- * walks), especially when returning to a function that depends
- * on the state of the region table, or statistics cursor.
- *
- * To avoid changing the semantics of the API, and the need for
- * a versioned symbol, maintain a flag indicating when a listing
- * has been carried out, and drop the region table before
- * returning.
- *
- * This ensures compatibility with programs compiled against
- * earlier versions of libdm.
- */
- if (!dms->regions && !(listed = dm_stats_list(dms, dms->program_id))) {
- log_error("Could not obtain region list while deleting "
- "region ID " FMTu64, region_id);
- goto bad;
- }
-
- if (!dm_stats_get_nr_regions(dms)) {
- log_error("Could not delete region ID " FMTu64 ": "
- "no regions found", region_id);
- goto bad;
- }
-
- /* includes invalid and special region_id values */
- if (!dm_stats_region_present(dms, region_id)) {
- log_error("Region ID " FMTu64 " does not exist", region_id);
- goto bad;
- }
-
- if (!_stats_delete_region(dms, region_id))
- goto bad;
-
- if (!listed)
- /* wipe region and mark as not present */
- _stats_region_destroy(&dms->regions[region_id]);
- else
- /* return handle to prior state */
- _stats_regions_destroy(dms);
-
- return 1;
-bad:
- if (listed)
- _stats_regions_destroy(dms);
-
- return 0;
-}
-
-int dm_stats_clear_region(struct dm_stats *dms, uint64_t region_id)
-{
- char msg[STATS_MSG_BUF_LEN];
- struct dm_task *dmt;
-
- if (!_stats_bound(dms))
- return_0;
-
- if (dm_snprintf(msg, sizeof(msg), "@stats_clear " FMTu64, region_id) < 0) {
- log_error("Could not prepare @stats_clear message.");
- return 0;
- }
-
- dmt = _stats_send_message(dms, msg);
-
- if (!dmt)
- return_0;
-
- dm_task_destroy(dmt);
-
- return 1;
-}
-
-static struct dm_task *_stats_print_region(struct dm_stats *dms,
- uint64_t region_id, unsigned start_line,
- unsigned num_lines, unsigned clear)
-{
- /* @stats_print[_clear] <region_id> [<start_line> <num_lines>] */
- const char *err_fmt = "Could not prepare @stats_print %s.";
- char msg[STATS_MSG_BUF_LEN], lines[RANGE_LEN];
- struct dm_task *dmt = NULL;
-
- if (start_line || num_lines)
- if (dm_snprintf(lines, sizeof(lines),
- "%u %u", start_line, num_lines) < 0) {
- log_error(err_fmt, "row specification");
- return NULL;
- }
-
- if (dm_snprintf(msg, sizeof(msg), "@stats_print%s " FMTu64 " %s",
- (clear) ? "_clear" : "",
- region_id, (start_line || num_lines) ? lines : "") < 0) {
- log_error(err_fmt, "message");
- return NULL;
- }
-
- if (!(dmt = _stats_send_message(dms, msg)))
- return_NULL;
-
- return dmt;
-}
-
-char *dm_stats_print_region(struct dm_stats *dms, uint64_t region_id,
- unsigned start_line, unsigned num_lines,
- unsigned clear)
-{
- char *resp = NULL;
- struct dm_task *dmt = NULL;
- const char *response;
-
- if (!_stats_bound(dms))
- return_0;
-
- /*
- * FIXME: 'print' can be emulated for groups or aggregate regions
- * by populating the handle and emitting aggregate counter data
- * in the kernel print format.
- */
- if (region_id == DM_STATS_WALK_GROUP)
- return_0;
-
- dmt = _stats_print_region(dms, region_id,
- start_line, num_lines, clear);
-
- if (!dmt)
- return_0;
-
- if (!(response = dm_task_get_message_response(dmt)))
- goto_out;
-
- if (!(resp = dm_pool_strdup(dms->mem, response)))
- log_error("Could not allocate memory for response buffer.");
-out:
- dm_task_destroy(dmt);
-
- return resp;
-}
-
-void dm_stats_buffer_destroy(struct dm_stats *dms, char *buffer)
-{
- dm_pool_free(dms->mem, buffer);
-}
-
-uint64_t dm_stats_get_nr_regions(const struct dm_stats *dms)
-{
- if (!dms)
- return_0;
-
- if (!dms->regions)
- return 0;
-
- return dms->nr_regions;
-}
-
-uint64_t dm_stats_get_nr_groups(const struct dm_stats *dms)
-{
- uint64_t group_id, nr_groups = 0;
-
- if (!dms)
- return_0;
-
- /* no regions or groups? */
- if (!dms->regions || !dms->groups)
- return 0;
-
- for (group_id = 0; group_id <= dms->max_region; group_id++)
- if (dms->groups[group_id].group_id
- != DM_STATS_GROUP_NOT_PRESENT)
- nr_groups++;
-
- return nr_groups;
-}
-
-/**
- * Test whether region_id is present in this set of stats data.
- */
-int dm_stats_region_present(const struct dm_stats *dms, uint64_t region_id)
-{
- if (!dms->regions)
- return_0;
-
- if (region_id > dms->max_region)
- return 0;
-
- 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)
-{
- struct dm_stats_region *region = &dms->regions[region_id];
-
- if (!_stats_bound(dms))
- return_0;
-
- if (!region) {
- log_error("Cannot populate empty handle before dm_stats_list().");
- return 0;
- }
- if (!_stats_parse_region(dms, resp, region, region->timescale)) {
- log_error("Could not parse @stats_print message response.");
- return 0;
- }
- region->region_id = region_id;
- return 1;
-}
-
-int dm_stats_populate(struct dm_stats *dms, const char *program_id,
- uint64_t region_id)
-{
- int all_regions = (region_id == DM_STATS_REGIONS_ALL);
- struct dm_task *dmt = NULL; /* @stats_print task */
- uint64_t saved_flags; /* saved walk flags */
- const char *resp;
-
- /*
- * We are about do destroy and re-create the region table, so it
- * is safe to use the cursor embedded in the stats handle: just
- * save a copy of the current walk_flags to restore later.
- */
- saved_flags = dms->walk_flags;
-
- if (!_stats_bound(dms))
- return_0;
-
- if ((!all_regions) && (region_id & DM_STATS_WALK_GROUP)) {
- log_error("Invalid region_id for dm_stats_populate: "
- "DM_STATS_WALK_GROUP");
- return 0;
- }
-
- if (!dms->nr_regions) {
- log_error("No regions registered.");
- return 0;
- }
-
- /* allow zero-length program_id for populate */
- if (!program_id)
- program_id = dms->program_id;
-
- if (all_regions && !dm_stats_list(dms, program_id)) {
- log_error("Could not parse @stats_list response.");
- goto bad;
- } else if (!_stats_set_name_cache(dms)) {
- goto_bad;
- }
-
- dms->walk_flags = DM_STATS_WALK_REGION;
- dm_stats_walk_start(dms);
- do {
- region_id = (all_regions)
- ? dm_stats_get_current_region(dms) : region_id;
-
- /* obtain all lines and clear counter values */
- if (!(dmt = _stats_print_region(dms, region_id, 0, 0, 1)))
- goto_bad;
-
- resp = dm_task_get_message_response(dmt);
- if (!_dm_stats_populate_region(dms, region_id, resp)) {
- dm_task_destroy(dmt);
- goto_bad;
- }
-
- dm_task_destroy(dmt);
- dm_stats_walk_next(dms);
-
- } while (all_regions && !dm_stats_walk_end(dms));
-
- dms->walk_flags = saved_flags;
- return 1;
-
-bad:
- dms->walk_flags = saved_flags;
- _stats_regions_destroy(dms);
- dms->regions = NULL;
- return 0;
-}
-
-/**
- * destroy a dm_stats object and all associated regions and counter sets.
- */
-void dm_stats_destroy(struct dm_stats *dms)
-{
- if (!dms)
- return;
-
- _stats_regions_destroy(dms);
- _stats_groups_destroy(dms);
- _stats_clear_binding(dms);
- dm_pool_destroy(dms->mem);
- dm_pool_destroy(dms->hist_mem);
- dm_pool_destroy(dms->group_mem);
- free(dms->program_id);
- free((char *) dms->name);
- free(dms);
-}
-
-/*
- * Walk each area that is a member of region_id rid.
- * i is a variable of type int that holds the current area_id.
- */
-#define _foreach_region_area(dms, rid, i) \
-for ((i) = 0; (i) < _nr_areas_region(&dms->regions[(rid)]); (i)++) \
-
-/*
- * Walk each region that is a member of group_id gid.
- * i is a variable of type int that holds the current region_id.
- */
-#define _foreach_group_region(dms, gid, i) \
-for ((i) = dm_bit_get_first((dms)->groups[(gid)].regions); \
- (i) != DM_STATS_GROUP_NOT_PRESENT; \
- (i) = dm_bit_get_next((dms)->groups[(gid)].regions, (i))) \
-
-/*
- * Walk each region that is a member of group_id gid visiting each
- * area within the region.
- * i is a variable of type int that holds the current region_id.
- * j is a variable of type int variable that holds the current area_id.
- */
-#define _foreach_group_area(dms, gid, i, j) \
-_foreach_group_region(dms, gid, i) \
- _foreach_region_area(dms, i, j)
-
-static uint64_t _stats_get_counter(const struct dm_stats *dms,
- const struct dm_stats_counters *area,
- dm_stats_counter_t counter)
-{
- switch(counter) {
- case DM_STATS_READS_COUNT:
- return area->reads;
- case DM_STATS_READS_MERGED_COUNT:
- return area->reads_merged;
- case DM_STATS_READ_SECTORS_COUNT:
- return area->read_sectors;
- case DM_STATS_READ_NSECS:
- return area->read_nsecs;
- case DM_STATS_WRITES_COUNT:
- return area->writes;
- case DM_STATS_WRITES_MERGED_COUNT:
- return area->writes_merged;
- case DM_STATS_WRITE_SECTORS_COUNT:
- return area->write_sectors;
- case DM_STATS_WRITE_NSECS:
- return area->write_nsecs;
- case DM_STATS_IO_IN_PROGRESS_COUNT:
- return area->io_in_progress;
- case DM_STATS_IO_NSECS:
- return area->io_nsecs;
- case DM_STATS_WEIGHTED_IO_NSECS:
- return area->weighted_io_nsecs;
- case DM_STATS_TOTAL_READ_NSECS:
- return area->total_read_nsecs;
- case DM_STATS_TOTAL_WRITE_NSECS:
- return area->total_write_nsecs;
- case DM_STATS_NR_COUNTERS:
- default:
- log_error("Attempt to read invalid counter: %d", counter);
- }
- return 0;
-}
-
-uint64_t dm_stats_get_counter(const struct dm_stats *dms,
- dm_stats_counter_t counter,
- uint64_t region_id, uint64_t area_id)
-{
- uint64_t i, j, sum = 0; /* aggregation */
- int sum_regions = 0;
- struct dm_stats_region *region;
- struct dm_stats_counters *area;
-
- region_id = (region_id == DM_STATS_REGION_CURRENT)
- ? dms->cur_region : region_id ;
- area_id = (area_id == DM_STATS_REGION_CURRENT)
- ? dms->cur_area : area_id ;
-
- sum_regions = !!(region_id & DM_STATS_WALK_GROUP);
-
- if (region_id == DM_STATS_WALK_GROUP)
- /* group walk using the cursor */
- region_id = dms->cur_group;
- else if (region_id & DM_STATS_WALK_GROUP)
- /* group walk using immediate group_id */
- region_id &= ~DM_STATS_WALK_GROUP;
- region = &dms->regions[region_id];
-
- /*
- * All statistics aggregation takes place here: aggregate metrics
- * are calculated as normal using the aggregated counter values
- * returned for the region or group specified.
- */
-
- if (_stats_region_is_grouped(dms, region_id) && (sum_regions)) {
- /* group */
- if (area_id & DM_STATS_WALK_GROUP)
- _foreach_group_area(dms, region->group_id, i, j) {
- area = &dms->regions[i].counters[j];
- sum += _stats_get_counter(dms, area, counter);
- }
- else
- _foreach_group_region(dms, region->group_id, i) {
- area = &dms->regions[i].counters[area_id];
- sum += _stats_get_counter(dms, area, counter);
- }
- } else if (area_id == DM_STATS_WALK_REGION) {
- /* aggregate region */
- _foreach_region_area(dms, region_id, j) {
- area = &dms->regions[region_id].counters[j];
- sum += _stats_get_counter(dms, area, counter);
- }
- } else {
- /* plain region / area */
- area = ®ion->counters[area_id];
- sum = _stats_get_counter(dms, area, counter);
- }
-
- return sum;
-}
-
-/*
- * Methods for accessing named counter fields. All methods share the
- * following naming scheme and prototype:
- *
- * uint64_t dm_stats_get_COUNTER(const struct dm_stats *, uint64_t, uint64_t)
- *
- * Where the two integer arguments are the region_id and area_id
- * respectively.
- *
- * name is the name of the counter (lower case)
- * counter is the part of the enum name following DM_STATS_ (upper case)
- */
-#define MK_STATS_GET_COUNTER_FN(name, counter) \
-uint64_t dm_stats_get_ ## name(const struct dm_stats *dms, \
- uint64_t region_id, uint64_t area_id) \
-{ \
- return dm_stats_get_counter(dms, DM_STATS_ ## counter, \
- region_id, area_id); \
-}
-
-MK_STATS_GET_COUNTER_FN(reads, READS_COUNT)
-MK_STATS_GET_COUNTER_FN(reads_merged, READS_MERGED_COUNT)
-MK_STATS_GET_COUNTER_FN(read_sectors, READ_SECTORS_COUNT)
-MK_STATS_GET_COUNTER_FN(read_nsecs, READ_NSECS)
-MK_STATS_GET_COUNTER_FN(writes, WRITES_COUNT)
-MK_STATS_GET_COUNTER_FN(writes_merged, WRITES_MERGED_COUNT)
-MK_STATS_GET_COUNTER_FN(write_sectors, WRITE_SECTORS_COUNT)
-MK_STATS_GET_COUNTER_FN(write_nsecs, WRITE_NSECS)
-MK_STATS_GET_COUNTER_FN(io_in_progress, IO_IN_PROGRESS_COUNT)
-MK_STATS_GET_COUNTER_FN(io_nsecs, IO_NSECS)
-MK_STATS_GET_COUNTER_FN(weighted_io_nsecs, WEIGHTED_IO_NSECS)
-MK_STATS_GET_COUNTER_FN(total_read_nsecs, TOTAL_READ_NSECS)
-MK_STATS_GET_COUNTER_FN(total_write_nsecs, TOTAL_WRITE_NSECS)
-#undef MK_STATS_GET_COUNTER_FN
-
-/*
- * Floating point stats metric functions
- *
- * Called from dm_stats_get_metric() to calculate the value of
- * the requested metric.
- *
- * int _metric_name(const struct dm_stats *dms,
- * struct dm_stats_counters *c,
- * double *value);
- *
- * Calculate a metric value from the counter data for the given
- * identifiers and store it in the memory pointed to by value,
- * applying group or region aggregation if enabled.
- *
- * Return one on success or zero on failure.
- *
- * To add a new metric:
- *
- * o Add a new name to the dm_stats_metric_t enum.
- * o Create a _metric_fn() to calculate the new metric.
- * o Add _metric_fn to the _metrics function table
- * (entries in enum order).
- * o Do not add a new named public function for the metric -
- * users of new metrics are encouraged to convert to the enum
- * based metric interface.
- *
- */
-
-static int _rd_merges_per_sec(const struct dm_stats *dms, double *rrqm,
- uint64_t region_id, uint64_t area_id)
-{
- double mrgs;
- mrgs = (double) dm_stats_get_counter(dms, DM_STATS_READS_MERGED_COUNT,
- region_id, area_id);
-
- *rrqm = mrgs / (double) dms->interval_ns;
-
- return 1;
-}
-
-static int _wr_merges_per_sec(const struct dm_stats *dms, double *wrqm,
- uint64_t region_id, uint64_t area_id)
-{
- double mrgs;
- mrgs = (double) dm_stats_get_counter(dms, DM_STATS_WRITES_MERGED_COUNT,
- region_id, area_id);
-
- *wrqm = mrgs / (double) dms->interval_ns;
-
- return 1;
-}
-
-static int _reads_per_sec(const struct dm_stats *dms, double *rd_s,
- uint64_t region_id, uint64_t area_id)
-{
- double reads;
- reads = (double) dm_stats_get_counter(dms, DM_STATS_READS_COUNT,
- region_id, area_id);
-
- *rd_s = (reads * NSEC_PER_SEC) / (double) dms->interval_ns;
-
- return 1;
-}
-
-static int _writes_per_sec(const struct dm_stats *dms, double *wr_s,
- uint64_t region_id, uint64_t area_id)
-{
- double writes;
- writes = (double) dm_stats_get_counter(dms, DM_STATS_WRITES_COUNT,
- region_id, area_id);
-
- *wr_s = (writes * NSEC_PER_SEC) / (double) dms->interval_ns;
-
- return 1;
-}
-
-static int _read_sectors_per_sec(const struct dm_stats *dms, double *rsec_s,
- uint64_t region_id, uint64_t area_id)
-{
- double sect;
- sect = (double) dm_stats_get_counter(dms, DM_STATS_READ_SECTORS_COUNT,
- region_id, area_id);
-
- *rsec_s = (sect * (double) NSEC_PER_SEC) / (double) dms->interval_ns;
-
- return 1;
-}
-
-static int _write_sectors_per_sec(const struct dm_stats *dms, double *wsec_s,
- uint64_t region_id, uint64_t area_id)
-{
- double sect;
- sect = (double) dm_stats_get_counter(dms, DM_STATS_WRITE_SECTORS_COUNT,
- region_id, area_id);
-
- *wsec_s = (sect * (double) NSEC_PER_SEC) / (double) dms->interval_ns;
-
- return 1;
-}
-
-static int _average_request_size(const struct dm_stats *dms, double *arqsz,
- uint64_t region_id, uint64_t area_id)
-{
- double ios, sectors;
-
- ios = (double) (dm_stats_get_counter(dms, DM_STATS_READS_COUNT,
- region_id, area_id)
- + dm_stats_get_counter(dms, DM_STATS_WRITES_COUNT,
- region_id, area_id));
- sectors = (double) (dm_stats_get_counter(dms, DM_STATS_READ_SECTORS_COUNT,
- region_id, area_id)
- + dm_stats_get_counter(dms, DM_STATS_WRITE_SECTORS_COUNT,
- region_id, area_id));
-
- if (ios > 0.0)
- *arqsz = sectors / ios;
- else
- *arqsz = 0.0;
-
- return 1;
-}
-
-static int _average_queue_size(const struct dm_stats *dms, double *qusz,
- uint64_t region_id, uint64_t area_id)
-{
- double io_ticks;
- io_ticks = (double) dm_stats_get_counter(dms, DM_STATS_WEIGHTED_IO_NSECS,
- region_id, area_id);
-
- if (io_ticks > 0.0)
- *qusz = io_ticks / (double) dms->interval_ns;
- else
- *qusz = 0.0;
-
- return 1;
-}
-
-static int _average_wait_time(const struct dm_stats *dms, double *await,
- uint64_t region_id, uint64_t area_id)
-{
- uint64_t io_ticks, nr_ios;
-
- io_ticks = dm_stats_get_counter(dms, DM_STATS_READ_NSECS,
- region_id, area_id);
- io_ticks += dm_stats_get_counter(dms, DM_STATS_WRITE_NSECS,
- region_id, area_id);
-
- nr_ios = dm_stats_get_counter(dms, DM_STATS_READS_COUNT,
- region_id, area_id);
- nr_ios += dm_stats_get_counter(dms, DM_STATS_WRITES_COUNT,
- region_id, area_id);
-
- if (nr_ios > 0)
- *await = (double) io_ticks / (double) nr_ios;
- else
- *await = 0.0;
-
- return 1;
-}
-
-static int _average_rd_wait_time(const struct dm_stats *dms, double *await,
- uint64_t region_id, uint64_t area_id)
-{
- uint64_t rd_io_ticks, nr_rd_ios;
-
- rd_io_ticks = dm_stats_get_counter(dms, DM_STATS_READ_NSECS,
- region_id, area_id);
- nr_rd_ios = dm_stats_get_counter(dms, DM_STATS_READS_COUNT,
- region_id, area_id);
-
- /*
- * If rd_io_ticks is > 0 this should imply that nr_rd_ios is
- * also > 0 (unless a kernel bug exists). Test for both here
- * before using the IO count as a divisor (Coverity).
- */
- if (rd_io_ticks > 0 && nr_rd_ios > 0)
- *await = (double) rd_io_ticks / (double) nr_rd_ios;
- else
- *await = 0.0;
-
- return 1;
-}
-
-static int _average_wr_wait_time(const struct dm_stats *dms, double *await,
- uint64_t region_id, uint64_t area_id)
-{
- uint64_t wr_io_ticks, nr_wr_ios;
-
- wr_io_ticks = dm_stats_get_counter(dms, DM_STATS_WRITE_NSECS,
- region_id, area_id);
- nr_wr_ios = dm_stats_get_counter(dms, DM_STATS_WRITES_COUNT,
- region_id, area_id);
-
- /*
- * If wr_io_ticks is > 0 this should imply that nr_wr_ios is
- * also > 0 (unless a kernel bug exists). Test for both here
- * before using the IO count as a divisor (Coverity).
- */
- if (wr_io_ticks > 0 && nr_wr_ios > 0)
- *await = (double) wr_io_ticks / (double) nr_wr_ios;
- else
- *await = 0.0;
-
- return 1;
-}
-
-static int _throughput(const struct dm_stats *dms, double *tput,
- uint64_t region_id, uint64_t area_id)
-{
- uint64_t nr_ios;
-
- nr_ios = dm_stats_get_counter(dms, DM_STATS_READS_COUNT,
- region_id, area_id);
- nr_ios += dm_stats_get_counter(dms, DM_STATS_WRITES_COUNT,
- region_id, area_id);
-
- *tput = ((double) NSEC_PER_SEC * (double) nr_ios)
- / (double) (dms->interval_ns);
-
- return 1;
-}
-
-static int _utilization(const struct dm_stats *dms, double *util,
- uint64_t region_id, uint64_t area_id)
-{
- uint64_t io_nsecs, interval_ns = dms->interval_ns;
-
- /**
- * If io_nsec > interval_ns 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.
- *
- * A zero interval_ns is also an error since metrics cannot be
- * calculated without a defined interval - return zero and emit a
- * backtrace in this case.
- */
- io_nsecs = dm_stats_get_counter(dms, DM_STATS_IO_NSECS,
- region_id, area_id);
-
- if (!interval_ns) {
- *util = 0.0;
- return_0;
- }
-
- io_nsecs = ((io_nsecs < interval_ns) ? io_nsecs : interval_ns);
-
- *util = (double) io_nsecs / (double) interval_ns;
-
- return 1;
-}
-
-static int _service_time(const struct dm_stats *dms, double *svctm,
- uint64_t region_id, uint64_t area_id)
-{
- double tput, util;
-
- if (!_throughput(dms, &tput, region_id, area_id))
- return 0;
-
- if (!_utilization(dms, &util, region_id, area_id))
- return 0;
-
- util *= 100;
-
- /* avoid NAN with zero counter values */
- if ( (uint64_t) tput == 0 || (uint64_t) util == 0) {
- *svctm = 0.0;
- return 1;
- }
-
- *svctm = ((double) NSEC_PER_SEC * dm_percent_to_float(util))
- / (100.0 * tput);
-
- return 1;
-}
-
-/*
- * Table in enum order:
- * DM_STATS_RD_MERGES_PER_SEC,
- * DM_STATS_WR_MERGES_PER_SEC,
- * DM_STATS_READS_PER_SEC,
- * DM_STATS_WRITES_PER_SEC,
- * DM_STATS_READ_SECTORS_PER_SEC,
- * DM_STATS_WRITE_SECTORS_PER_SEC,
- * DM_STATS_AVERAGE_REQUEST_SIZE,
- * DM_STATS_AVERAGE_QUEUE_SIZE,
- * DM_STATS_AVERAGE_WAIT_TIME,
- * DM_STATS_AVERAGE_RD_WAIT_TIME,
- * DM_STATS_AVERAGE_WR_WAIT_TIME
- * DM_STATS_SERVICE_TIME,
- * DM_STATS_THROUGHPUT,
- * DM_STATS_UTILIZATION
- *
-*/
-
-typedef int (*_metric_fn_t)(const struct dm_stats *, double *,
- uint64_t, uint64_t);
-
-_metric_fn_t _metrics[DM_STATS_NR_METRICS] = {
- _rd_merges_per_sec,
- _wr_merges_per_sec,
- _reads_per_sec,
- _writes_per_sec,
- _read_sectors_per_sec,
- _write_sectors_per_sec,
- _average_request_size,
- _average_queue_size,
- _average_wait_time,
- _average_rd_wait_time,
- _average_wr_wait_time,
- _service_time,
- _throughput,
- _utilization
-};
-
-int dm_stats_get_metric(const struct dm_stats *dms, int metric,
- uint64_t region_id, uint64_t area_id, double *value)
-{
- if (!dms->interval_ns)
- return_0;
-
- /*
- * Decode DM_STATS_{REGION,AREA}_CURRENT here; counters will then
- * be returned for the actual current region and area.
- *
- * DM_STATS_WALK_GROUP is passed through to the counter methods -
- * aggregates for the group are returned and used to calculate
- * the metric for the group totals.
- */
- region_id = (region_id == DM_STATS_REGION_CURRENT)
- ? dms->cur_region : region_id ;
- area_id = (area_id == DM_STATS_REGION_CURRENT)
- ? dms->cur_area : area_id ;
-
- if (metric < 0 || metric >= DM_STATS_NR_METRICS) {
- log_error("Attempt to read invalid metric: %d", metric);
- return 0;
- }
-
- return _metrics[metric](dms, value, region_id, area_id);
-}
-
-/**
- * Methods for accessing stats metrics. All methods share the
- * following naming scheme and prototype:
- *
- * uint64_t dm_stats_get_metric(struct dm_stats *,
- * int, int,
- * uint64_t, uint64_t,
- * double *v)
- *
- * Where the two integer arguments are the region_id and area_id
- * respectively.
- *
- * name is the name of the metric (lower case)
- * metric is the part of the enum name following DM_STATS_ (upper case)
- */
-#define MK_STATS_GET_METRIC_FN(name, metric, meta) \
-int dm_stats_get_ ## name(const struct dm_stats *dms, double *meta, \
- uint64_t region_id, uint64_t area_id) \
-{ \
- return dm_stats_get_metric(dms, DM_STATS_ ## metric, \
- region_id, area_id, meta); \
-}
-
-MK_STATS_GET_METRIC_FN(rd_merges_per_sec, RD_MERGES_PER_SEC, rrqm)
-MK_STATS_GET_METRIC_FN(wr_merges_per_sec, WR_MERGES_PER_SEC, wrqm)
-MK_STATS_GET_METRIC_FN(reads_per_sec, READS_PER_SEC, rd_s)
-MK_STATS_GET_METRIC_FN(writes_per_sec, WRITES_PER_SEC, wr_s)
-MK_STATS_GET_METRIC_FN(read_sectors_per_sec, READ_SECTORS_PER_SEC, rsec_s)
-MK_STATS_GET_METRIC_FN(write_sectors_per_sec, WRITE_SECTORS_PER_SEC, wsec_s)
-MK_STATS_GET_METRIC_FN(average_request_size, AVERAGE_REQUEST_SIZE, arqsz)
-MK_STATS_GET_METRIC_FN(average_queue_size, AVERAGE_QUEUE_SIZE, qusz)
-MK_STATS_GET_METRIC_FN(average_wait_time, AVERAGE_WAIT_TIME, await)
-MK_STATS_GET_METRIC_FN(average_rd_wait_time, AVERAGE_RD_WAIT_TIME, await)
-MK_STATS_GET_METRIC_FN(average_wr_wait_time, AVERAGE_WR_WAIT_TIME, await)
-MK_STATS_GET_METRIC_FN(service_time, SERVICE_TIME, svctm)
-MK_STATS_GET_METRIC_FN(throughput, THROUGHPUT, tput)
-
-/*
- * Utilization is an exception since it used the dm_percent_t type in the
- * original named function based interface: preserve this behaviour for
- * backwards compatibility with existing users.
- *
- * The same metric may be accessed as a double via the enum based metric
- * interface.
- */
-int dm_stats_get_utilization(const struct dm_stats *dms, dm_percent_t *util,
- uint64_t region_id, uint64_t area_id)
-{
- double _util;
-
- if (!dm_stats_get_metric(dms, DM_STATS_UTILIZATION,
- region_id, area_id, &_util))
- return_0;
- /* scale up utilization value in the range [0.00..1.00] */
- *util = dm_make_percent(DM_PERCENT_1 * _util, DM_PERCENT_1);
- return 1;
-}
-
-void dm_stats_set_sampling_interval_ms(struct dm_stats *dms, uint64_t interval_ms)
-{
- /* All times use nsecs internally. */
- dms->interval_ns = interval_ms * NSEC_PER_MSEC;
-}
-
-void dm_stats_set_sampling_interval_ns(struct dm_stats *dms, uint64_t interval_ns)
-{
- dms->interval_ns = interval_ns;
-}
-
-uint64_t dm_stats_get_sampling_interval_ms(const struct dm_stats *dms)
-{
- /* All times use nsecs internally. */
- return (dms->interval_ns / NSEC_PER_MSEC);
-}
-
-uint64_t dm_stats_get_sampling_interval_ns(const struct dm_stats *dms)
-{
- /* All times use nsecs internally. */
- return (dms->interval_ns);
-}
-
-int dm_stats_set_program_id(struct dm_stats *dms, int allow_empty,
- const char *program_id)
-{
- if (!allow_empty && (!program_id || !strlen(program_id))) {
- log_error("Empty program_id not permitted without "
- "allow_empty=1");
- return 0;
- }
-
- if (!program_id)
- program_id = "";
-
- free(dms->program_id);
-
- if (!(dms->program_id = strdup(program_id)))
- return_0;
-
- return 1;
-}
-
-uint64_t dm_stats_get_current_region(const struct dm_stats *dms)
-{
- return dms->cur_region;
-}
-
-uint64_t dm_stats_get_current_area(const struct dm_stats *dms)
-{
- return dms->cur_area & ~DM_STATS_WALK_ALL;
-}
-
-int dm_stats_get_region_start(const struct dm_stats *dms, uint64_t *start,
- uint64_t region_id)
-{
- if (!dms || !dms->regions)
- return_0;
-
- /* start is unchanged when aggregating areas */
- if (region_id & DM_STATS_WALK_REGION)
- region_id &= ~DM_STATS_WALK_REGION;
-
- /* use start of first region as group start */
- if (region_id & DM_STATS_WALK_GROUP) {
- if (region_id == DM_STATS_WALK_GROUP)
- region_id = dms->cur_group;
- else
- region_id &= ~DM_STATS_WALK_GROUP;
- }
-
- *start = dms->regions[region_id].start;
- return 1;
-}
-
-int dm_stats_get_region_len(const struct dm_stats *dms, uint64_t *len,
- uint64_t region_id)
-{
- uint64_t i;
- if (!dms || !dms->regions)
- return_0;
-
- *len = 0;
-
- /* length is unchanged when aggregating areas */
- if (region_id & DM_STATS_WALK_REGION)
- region_id &= ~DM_STATS_WALK_REGION;
-
- if (region_id & DM_STATS_WALK_GROUP) {
- /* decode region / group ID */
- if (region_id == DM_STATS_WALK_GROUP)
- region_id = dms->cur_group;
- else
- region_id &= ~DM_STATS_WALK_GROUP;
-
- /* use sum of region sizes as group size */
- if (_stats_region_is_grouped(dms, region_id))
- _foreach_group_region(dms, dms->cur_group, i)
- *len += dms->regions[i].len;
- else {
- log_error("Group ID " FMTu64 " does not exist",
- region_id);
- return 0;
- }
- } else
- *len = dms->regions[region_id].len;
-
- return 1;
-}
-
-int dm_stats_get_region_area_len(const struct dm_stats *dms, uint64_t *len,
- uint64_t region_id)
-{
- if (!dms || !dms->regions)
- return_0;
-
- /* groups are not subdivided - area size equals group size */
- if (region_id & (DM_STATS_WALK_GROUP | DM_STATS_WALK_REGION))
- /* get_region_len will decode region_id */
- return dm_stats_get_region_len(dms, len, region_id);
-
- *len = dms->regions[region_id].step;
- return 1;
-}
-
-int dm_stats_get_current_region_start(const struct dm_stats *dms,
- uint64_t *start)
-{
- return dm_stats_get_region_start(dms, start, dms->cur_region);
-}
-
-int dm_stats_get_current_region_len(const struct dm_stats *dms,
- uint64_t *len)
-{
- return dm_stats_get_region_len(dms, len, dms->cur_region);
-}
-
-int dm_stats_get_current_region_area_len(const struct dm_stats *dms,
- uint64_t *step)
-{
- return dm_stats_get_region_area_len(dms, step, dms->cur_region);
-}
-
-int dm_stats_get_area_start(const struct dm_stats *dms, uint64_t *start,
- uint64_t region_id, uint64_t area_id)
-{
- struct dm_stats_region *region;
- if (!dms || !dms->regions)
- return_0;
-
- /* group or region area start equals region start */
- if (region_id & (DM_STATS_WALK_GROUP | DM_STATS_WALK_REGION))
- return dm_stats_get_region_start(dms, start, region_id);
-
- region = &dms->regions[region_id];
- *start = region->start + region->step * area_id;
- return 1;
-}
-
-int dm_stats_get_area_offset(const struct dm_stats *dms, uint64_t *offset,
- uint64_t region_id, uint64_t area_id)
-{
- if (!dms || !dms->regions)
- return_0;
-
- /* no areas for groups or aggregate regions */
- if (region_id & (DM_STATS_WALK_GROUP | DM_STATS_WALK_REGION))
- *offset = 0;
- else
- *offset = dms->regions[region_id].step * area_id;
-
- return 1;
-}
-
-int dm_stats_get_current_area_start(const struct dm_stats *dms,
- uint64_t *start)
-{
- return dm_stats_get_area_start(dms, start,
- dms->cur_region, dms->cur_area);
-}
-
-int dm_stats_get_current_area_offset(const struct dm_stats *dms,
- uint64_t *offset)
-{
- return dm_stats_get_area_offset(dms, offset,
- dms->cur_region, dms->cur_area);
-}
-
-int dm_stats_get_current_area_len(const struct dm_stats *dms,
- uint64_t *len)
-{
- return dm_stats_get_region_area_len(dms, len, dms->cur_region);
-}
-
-const char *dm_stats_get_region_program_id(const struct dm_stats *dms,
- uint64_t region_id)
-{
- const char *program_id = NULL;
-
- if (region_id & DM_STATS_WALK_GROUP)
- return dms->program_id;
-
- if (region_id & DM_STATS_WALK_REGION)
- region_id &= ~DM_STATS_WALK_REGION;
-
- program_id = dms->regions[region_id].program_id;
- return (program_id) ? program_id : "";
-}
-
-const char *dm_stats_get_region_aux_data(const struct dm_stats *dms,
- uint64_t region_id)
-{
- const char *aux_data = NULL;
-
- if (region_id & DM_STATS_WALK_GROUP)
- return "";
-
- if (region_id & DM_STATS_WALK_REGION)
- region_id &= ~DM_STATS_WALK_REGION;
-
- aux_data = dms->regions[region_id].aux_data;
- return (aux_data) ? aux_data : "" ;
-}
-
-int dm_stats_set_alias(struct dm_stats *dms, uint64_t group_id, const char *alias)
-{
- struct dm_stats_group *group = NULL;
- const char *old_alias = NULL;
-
- if (!dms->regions || !dms->groups || !alias)
- return_0;
-
- if (!_stats_region_is_grouped(dms, group_id)) {
- log_error("Cannot set alias for ungrouped region ID "
- FMTu64, group_id);
- return 0;
- }
-
- if (group_id & DM_STATS_WALK_GROUP) {
- if (group_id == DM_STATS_WALK_GROUP)
- group_id = dms->cur_group;
- else
- group_id &= ~DM_STATS_WALK_GROUP;
- }
-
- if (group_id != dms->regions[group_id].group_id) {
- /* dm_stats_set_alias() must be called on the group ID. */
- log_error("Cannot set alias for group member " FMTu64 ".",
- group_id);
- return 0;
- }
-
- group = &dms->groups[group_id];
- old_alias = group->alias;
-
- group->alias = strdup(alias);
- if (!group->alias) {
- log_error("Could not allocate memory for alias.");
- goto bad;
- }
-
- if (!_stats_set_aux(dms, group_id, dms->regions[group_id].aux_data)) {
- log_error("Could not set new aux_data");
- goto bad;
- }
-
- free((char *) old_alias);
-
- return 1;
-
-bad:
- free((char *) group->alias);
- group->alias = old_alias;
- return 0;
-}
-
-const char *dm_stats_get_alias(const struct dm_stats *dms, uint64_t id)
-{
- const struct dm_stats_region *region;
-
- id = (id == DM_STATS_REGION_CURRENT) ? dms->cur_region : id;
-
- if (id & DM_STATS_WALK_GROUP) {
- if (id == DM_STATS_WALK_GROUP)
- id = dms->cur_group;
- else
- id &= ~DM_STATS_WALK_GROUP;
- }
-
- region = &dms->regions[id];
- if (!_stats_region_is_grouped(dms, id)
- || !dms->groups[region->group_id].alias)
- return dms->name;
-
- return dms->groups[region->group_id].alias;
-}
-
-const char *dm_stats_get_current_region_program_id(const struct dm_stats *dms)
-{
- return dm_stats_get_region_program_id(dms, dms->cur_region);
-}
-
-const char *dm_stats_get_current_region_aux_data(const struct dm_stats *dms)
-{
- return dm_stats_get_region_aux_data(dms, dms->cur_region);
-}
-
-int dm_stats_get_region_precise_timestamps(const struct dm_stats *dms,
- uint64_t region_id)
-{
- struct dm_stats_region *region;
-
- if (region_id == DM_STATS_REGION_CURRENT)
- region_id = dms->cur_region;
-
- if (region_id == DM_STATS_WALK_GROUP)
- region_id = dms->cur_group;
- else if (region_id & DM_STATS_WALK_GROUP)
- region_id &= ~DM_STATS_WALK_GROUP;
-
- region = &dms->regions[region_id];
- return region->timescale == 1;
-}
-
-int dm_stats_get_current_region_precise_timestamps(const struct dm_stats *dms)
-{
- return dm_stats_get_region_precise_timestamps(dms,
- DM_STATS_REGION_CURRENT);
-}
-
-/*
- * Histogram access methods.
- */
-
-static void _sum_histogram_bins(const struct dm_stats *dms,
- struct dm_histogram *dmh_aggr,
- uint64_t region_id, uint64_t area_id)
-{
- struct dm_stats_region *region;
- struct dm_histogram_bin *bins;
- struct dm_histogram *dmh_cur;
- int bin;
-
- region = &dms->regions[region_id];
- dmh_cur = region->counters[area_id].histogram;
- bins = dmh_aggr->bins;
-
- for (bin = 0; bin < dmh_aggr->nr_bins; bin++)
- bins[bin].count += dmh_cur->bins[bin].count;
-}
-
-/*
- * Create an aggregate histogram for a sub-divided region or a group.
- */
-static struct dm_histogram *_aggregate_histogram(const struct dm_stats *dms,
- uint64_t region_id,
- uint64_t area_id)
-{
- struct dm_histogram *dmh_aggr, *dmh_cur, **dmh_cachep;
- uint64_t group_id = DM_STATS_GROUP_NOT_PRESENT;
- int bin, nr_bins, group = 1;
- size_t hist_size;
-
- if (area_id == DM_STATS_WALK_REGION) {
- /* region aggregation */
- group = 0;
- if (!_stats_region_present(&dms->regions[region_id]))
- return_NULL;
-
- if (!dms->regions[region_id].bounds)
- return_NULL;
-
- if (!dms->regions[region_id].counters)
- return dms->regions[region_id].bounds;
-
- if (dms->regions[region_id].histogram)
- return dms->regions[region_id].histogram;
-
- dmh_cur = dms->regions[region_id].counters[0].histogram;
- dmh_cachep = &dms->regions[region_id].histogram;
- nr_bins = dms->regions[region_id].bounds->nr_bins;
- } else {
- /* group aggregation */
- group_id = region_id;
- area_id = DM_STATS_WALK_GROUP;
- if (!_stats_group_id_present(dms, group_id))
- return_NULL;
-
- if (!dms->regions[group_id].bounds)
- return_NULL;
-
- if (!dms->regions[group_id].counters)
- return dms->regions[group_id].bounds;
-
- if (dms->groups[group_id].histogram)
- return dms->groups[group_id].histogram;
-
- dmh_cur = dms->regions[group_id].counters[0].histogram;
- dmh_cachep = &dms->groups[group_id].histogram;
- nr_bins = dms->regions[group_id].bounds->nr_bins;
- }
-
- hist_size = sizeof(*dmh_aggr)
- + nr_bins * sizeof(struct dm_histogram_bin);
-
- if (!(dmh_aggr = dm_pool_zalloc(dms->hist_mem, hist_size))) {
- log_error("Could not allocate group histogram");
- return 0;
- }
-
- dmh_aggr->nr_bins = dmh_cur->nr_bins;
- dmh_aggr->dms = dms;
-
- if (!group)
- _foreach_region_area(dms, region_id, area_id) {
- _sum_histogram_bins(dms, dmh_aggr, region_id, area_id);
- }
- else {
- _foreach_group_area(dms, group_id, region_id, area_id) {
- _sum_histogram_bins(dms, dmh_aggr, region_id, area_id);
- }
- }
-
- for (bin = 0; bin < nr_bins; bin++) {
- dmh_aggr->sum += dmh_aggr->bins[bin].count;
- dmh_aggr->bins[bin].upper = dmh_cur->bins[bin].upper;
- }
-
- /* cache aggregate histogram for subsequent access */
- *dmh_cachep = dmh_aggr;
-
- return dmh_aggr;
-}
-
-struct dm_histogram *dm_stats_get_histogram(const struct dm_stats *dms,
- uint64_t region_id,
- uint64_t area_id)
-{
- int aggr = 0;
-
- if (region_id == DM_STATS_REGION_CURRENT) {
- region_id = dms->cur_region;
- if (region_id & DM_STATS_WALK_GROUP) {
- region_id = dms->cur_group;
- aggr = 1;
- }
- } else if (region_id & DM_STATS_WALK_GROUP) {
- region_id &= ~DM_STATS_WALK_GROUP;
- aggr = 1;
- }
-
- area_id = (area_id == DM_STATS_AREA_CURRENT)
- ? dms->cur_area : area_id ;
-
- if (area_id == DM_STATS_WALK_REGION)
- aggr = 1;
-
- if (aggr)
- return _aggregate_histogram(dms, region_id, area_id);
-
- if (region_id & DM_STATS_WALK_REGION)
- region_id &= ~DM_STATS_WALK_REGION;
-
- if (!dms->regions[region_id].counters)
- return dms->regions[region_id].bounds;
-
- return dms->regions[region_id].counters[area_id].histogram;
-}
-
-int dm_histogram_get_nr_bins(const struct dm_histogram *dmh)
-{
- return dmh->nr_bins;
-}
-
-uint64_t dm_histogram_get_bin_lower(const struct dm_histogram *dmh, int bin)
-{
- return (!bin) ? 0 : dmh->bins[bin - 1].upper;
-}
-
-uint64_t dm_histogram_get_bin_upper(const struct dm_histogram *dmh, int bin)
-{
- return dmh->bins[bin].upper;
-}
-
-uint64_t dm_histogram_get_bin_width(const struct dm_histogram *dmh, int bin)
-{
- uint64_t upper, lower;
- upper = dm_histogram_get_bin_upper(dmh, bin);
- lower = dm_histogram_get_bin_lower(dmh, bin);
- return (upper - lower);
-}
-
-uint64_t dm_histogram_get_bin_count(const struct dm_histogram *dmh, int bin)
-{
- return dmh->bins[bin].count;
-}
-
-uint64_t dm_histogram_get_sum(const struct dm_histogram *dmh)
-{
- return dmh->sum;
-}
-
-dm_percent_t dm_histogram_get_bin_percent(const struct dm_histogram *dmh,
- int bin)
-{
- uint64_t value = dm_histogram_get_bin_count(dmh, bin);
- uint64_t width = dm_histogram_get_bin_width(dmh, bin);
- uint64_t total = dm_histogram_get_sum(dmh);
-
- double val = (double) value;
-
- if (!total || !value || !width)
- return DM_PERCENT_0;
-
- return dm_make_percent((uint64_t) val, total);
-}
-
-/*
- * Histogram string helper functions: used to construct histogram and
- * bin boundary strings from numeric data.
- */
-
-/*
- * Allocate an unbound histogram object with nr_bins bins. Only used
- * for histograms used to hold bounds values as arguments for calls to
- * dm_stats_create_region().
- */
-static struct dm_histogram *_alloc_dm_histogram(int nr_bins)
-{
- /* Allocate space for dm_histogram + nr_entries. */
- size_t size = sizeof(struct dm_histogram) +
- (unsigned) nr_bins * sizeof(struct dm_histogram_bin);
- return zalloc(size);
-}
-
-/*
- * Parse a histogram bounds string supplied by the user. The string
- * consists of a list of numbers, "n1,n2,n3,..." with optional 'ns',
- * 'us', 'ms', or 's' unit suffixes.
- *
- * The scale parameter indicates the timescale used for this region: one
- * for nanoscale resolution and NSEC_PER_MSEC for miliseconds.
- *
- * On return bounds contains a pointer to an array of uint64_t
- * histogram bounds values expressed in units of nanoseconds.
- */
-struct dm_histogram *dm_histogram_bounds_from_string(const char *bounds_str)
-{
- static const char _valid_chars[] = "0123456789,muns";
- uint64_t this_val = 0, mult = 1;
- const char *c, *v, *val_start;
- struct dm_histogram_bin *cur;
- struct dm_histogram *dmh;
- int nr_entries = 1;
- char *endptr;
-
- c = bounds_str;
-
- /* Count number of bounds entries. */
- while(*c)
- if (*(c++) == ',')
- nr_entries++;
-
- c = bounds_str;
-
- if (!(dmh = _alloc_dm_histogram(nr_entries)))
- return_0;
-
- dmh->nr_bins = nr_entries;
-
- cur = dmh->bins;
-
- do {
- for (v = _valid_chars; *v; v++)
- if (*c == *v)
- break;
-
- if (!*v) {
- stack;
- goto badchar;
- }
-
- if (*c == ',') {
- log_error("Empty histogram bin not allowed: %s",
- bounds_str);
- goto bad;
- } else {
- val_start = c;
- endptr = NULL;
-
- this_val = strtoull(val_start, &endptr, 10);
- if (!endptr) {
- log_error("Could not parse histogram bound.");
- goto bad;
- }
- c = endptr; /* Advance to units, comma, or end. */
-
- if (*c == 's') {
- mult = NSEC_PER_SEC;
- c++; /* Advance over 's'. */
- } else if (*(c + 1) == 's') {
- if (*c == 'm')
- mult = NSEC_PER_MSEC;
- else if (*c == 'u')
- mult = NSEC_PER_USEC;
- else if (*c == 'n')
- mult = 1;
- else {
- stack;
- goto badchar;
- }
- c += 2; /* Advance over 'ms', 'us', or 'ns'. */
- } else if (*c == ',')
- c++;
- else if (*c) { /* Expected ',' or NULL. */
- stack;
- goto badchar;
- }
-
- if (*c == ',')
- c++;
- this_val *= mult;
- (cur++)->upper = this_val;
- }
- } while (*c);
-
- /* Bounds histograms have no owner. */
- dmh->dms = NULL;
- dmh->region = NULL;
-
- return dmh;
-
-badchar:
- log_error("Invalid character in histogram: %c", *c);
-bad:
- free(dmh);
- return NULL;
-}
-
-struct dm_histogram *dm_histogram_bounds_from_uint64(const uint64_t *bounds)
-{
- const uint64_t *entry = bounds;
- struct dm_histogram_bin *cur;
- struct dm_histogram *dmh;
- int nr_entries = 1;
-
- if (!bounds || !bounds[0]) {
- log_error("Could not parse empty histogram bounds array");
- return 0;
- }
-
- /* Count number of bounds entries. */
- while(*entry)
- if (*(++entry))
- nr_entries++;
-
- entry = bounds;
-
- if (!(dmh = _alloc_dm_histogram(nr_entries)))
- return_0;
-
- dmh->nr_bins = nr_entries;
-
- cur = dmh->bins;
-
- while (*entry)
- (cur++)->upper = *(entry++);
-
- /* Bounds histograms have no owner. */
- dmh->dms = NULL;
- dmh->region = NULL;
-
- return dmh;
-}
-
-void dm_histogram_bounds_destroy(struct dm_histogram *bounds)
-{
- if (!bounds)
- return;
-
- /* Bounds histograms are not bound to any handle or region. */
- if (bounds->dms || bounds->region) {
- log_error("Freeing invalid histogram bounds pointer %p.",
- (void *) bounds);
- stack;
- }
- /* free() expects a (void *). */
- free((void *) bounds);
-}
-
-/*
- * Scale a bounds value down from nanoseconds to the largest possible
- * whole unit suffix.
- */
-static void _scale_bound_value_to_suffix(uint64_t *bound, const char **suffix)
-{
- *suffix = "ns";
- if (!(*bound % NSEC_PER_SEC)) {
- *bound /= NSEC_PER_SEC;
- *suffix = "s";
- } else if (!(*bound % NSEC_PER_MSEC)) {
- *bound /= NSEC_PER_MSEC;
- *suffix = "ms";
- } else if (!(*bound % NSEC_PER_USEC)) {
- *bound /= NSEC_PER_USEC;
- *suffix = "us";
- }
-}
-
-#define DM_HISTOGRAM_BOUNDS_MASK 0x30
-#define BOUNDS_LEN 64
-
-static int _make_bounds_string(char *buf, size_t size, uint64_t lower,
- uint64_t upper, int flags, int width)
-{
- char bound_buf[BOUNDS_LEN];
- const char *l_suff = NULL;
- const char *u_suff = NULL;
- const char *sep = "";
- int bounds = flags & DM_HISTOGRAM_BOUNDS_MASK;
-
- if (!bounds)
- return_0;
-
- *buf = '\0';
-
- if (flags & DM_HISTOGRAM_SUFFIX) {
- _scale_bound_value_to_suffix(&lower, &l_suff);
- _scale_bound_value_to_suffix(&upper, &u_suff);
- } else
- l_suff = u_suff = "";
-
- if (flags & DM_HISTOGRAM_VALUES)
- sep = ":";
-
- if (bounds > DM_HISTOGRAM_BOUNDS_LOWER) {
- /* Handle infinite uppermost bound. */
- if (upper == UINT64_MAX) {
- if (dm_snprintf(bound_buf, sizeof(bound_buf),
- ">" FMTu64 "%s", lower, l_suff) < 0)
- goto_out;
- /* Only display an 'upper' string for final bin. */
- bounds = DM_HISTOGRAM_BOUNDS_UPPER;
- } else {
- if (dm_snprintf(bound_buf, sizeof(bound_buf),
- FMTu64 "%s", upper, u_suff) < 0)
- goto_out;
- }
- } else if (bounds == DM_HISTOGRAM_BOUNDS_LOWER) {
- if ((dm_snprintf(bound_buf, sizeof(bound_buf), FMTu64 "%s",
- lower, l_suff)) < 0)
- goto_out;
- }
-
- switch (bounds) {
- case DM_HISTOGRAM_BOUNDS_LOWER:
- case DM_HISTOGRAM_BOUNDS_UPPER:
- return dm_snprintf(buf, size, "%*s%s", width, bound_buf, sep);
- case DM_HISTOGRAM_BOUNDS_RANGE:
- return dm_snprintf(buf, size, FMTu64 "%s-%s%s",
- lower, l_suff, bound_buf, sep);
- }
-out:
- return 0;
-}
-
-#define BOUND_WIDTH_NOSUFFIX 10 /* 999999999 nsecs */
-#define BOUND_WIDTH 6 /* bounds string up to 9999xs */
-#define COUNT_WIDTH 6 /* count string: up to 9999 */
-#define PERCENT_WIDTH 6 /* percent string : 0.00-100.00% */
-#define DM_HISTOGRAM_VALUES_MASK 0x06
-
-const char *dm_histogram_to_string(const struct dm_histogram *dmh, int bin,
- int width, int flags)
-{
- char buf[BOUNDS_LEN], bounds_buf[BOUNDS_LEN];
- int minwidth, bounds, values, start, last;
- uint64_t lower, upper, val_u64; /* bounds of the current bin. */
- /* Use the histogram pool for string building. */
- struct dm_pool *mem = dmh->dms->hist_mem;
- const char *sep = "";
- int bounds_width;
- ssize_t len = 0;
- float val_flt;
-
- bounds = flags & DM_HISTOGRAM_BOUNDS_MASK;
- values = flags & DM_HISTOGRAM_VALUES;
-
- if (bin < 0) {
- start = 0;
- last = dmh->nr_bins - 1;
- } else
- start = last = bin;
-
- minwidth = width;
-
- if (width < 0 || !values)
- width = minwidth = 0; /* no padding */
- else if (flags & DM_HISTOGRAM_PERCENT)
- width = minwidth = (width) ? : PERCENT_WIDTH;
- else if (flags & DM_HISTOGRAM_VALUES)
- width = minwidth = (width) ? : COUNT_WIDTH;
-
- if (values && !width)
- sep = ":";
-
- /* Set bounds string to the empty string. */
- bounds_buf[0] = '\0';
-
- if (!dm_pool_begin_object(mem, 64))
- return_0;
-
- for (bin = start; bin <= last; bin++) {
- if (bounds) {
- /* Default bounds width depends on time suffixes. */
- bounds_width = (!(flags & DM_HISTOGRAM_SUFFIX))
- ? BOUND_WIDTH_NOSUFFIX
- : BOUND_WIDTH ;
-
- bounds_width = (!width) ? width : bounds_width;
-
- lower = dm_histogram_get_bin_lower(dmh, bin);
- upper = dm_histogram_get_bin_upper(dmh, bin);
-
- len = sizeof(bounds_buf);
- len = _make_bounds_string(bounds_buf, len,
- lower, upper, flags,
- bounds_width);
- /*
- * Comma separates "bounds: value" pairs unless
- * --noheadings is used.
- */
- sep = (width || !values) ? "," : ":";
-
- /* Adjust width by real bounds length if set. */
- width -= (width) ? (len - (bounds_width + 1)) : 0;
-
- /* -ve width indicates specified width was overrun. */
- width = (width > 0) ? width : 0;
- }
-
- if (bin == last)
- sep = "";
-
- if (flags & DM_HISTOGRAM_PERCENT) {
- dm_percent_t pr;
- pr = dm_histogram_get_bin_percent(dmh, bin);
- val_flt = dm_percent_to_float(pr);
- len = dm_snprintf(buf, sizeof(buf), "%s%*.2f%%%s",
- bounds_buf, width, val_flt, sep);
- } else if (values) {
- val_u64 = dmh->bins[bin].count;
- len = dm_snprintf(buf, sizeof(buf), "%s%*"PRIu64"%s",
- bounds_buf, width, val_u64, sep);
- } else if (bounds)
- len = dm_snprintf(buf, sizeof(buf), "%s%s", bounds_buf,
- sep);
- else {
- *buf = '\0';
- len = 0;
- }
-
- if (len < 0)
- goto_bad;
-
- width = minwidth; /* re-set histogram column width. */
- if (!dm_pool_grow_object(mem, buf, (size_t) len))
- goto_bad;
- }
-
- if (!dm_pool_grow_object(mem, "\0", 1))
- goto_bad;
-
- return (const char *) dm_pool_end_object(mem);
-
-bad:
- dm_pool_abandon_object(mem);
- return NULL;
-}
-
-/*
- * A lightweight representation of an extent (region, area, file
- * system block or extent etc.). A table of extents can be used
- * to sort and to efficiently find holes or overlaps among a set
- * of tuples of the form (id, start, len).
- */
-struct _extent {
- struct dm_list list;
- uint64_t id;
- uint64_t start;
- uint64_t len;
-};
-
-/* last address in an extent */
-#define _extent_end(a) ((a)->start + (a)->len - 1)
-
-/* a and b must be sorted by increasing start sector */
-#define _extents_overlap(a, b) (_extent_end(a) > (b)->start)
-
-/*
- * Comparison function to sort extents in ascending start order.
- */
-static int _extent_start_compare(const void *p1, const void *p2)
-{
- const struct _extent *r1, *r2;
- r1 = (const struct _extent *) p1;
- r2 = (const struct _extent *) p2;
-
- if (r1->start < r2->start)
- return -1;
- else if (r1->start == r2->start)
- return 0;
- return 1;
-}
-
-static int _stats_create_group(struct dm_stats *dms, dm_bitset_t regions,
- const char *alias, uint64_t *group_id)
-{
- struct dm_stats_group *group;
- *group_id = dm_bit_get_first(regions);
-
- /* group has no regions? */
- if (*group_id == DM_STATS_GROUP_NOT_PRESENT)
- return_0;
-
- group = &dms->groups[*group_id];
-
- if (group->regions) {
- log_error(INTERNAL_ERROR "Unexpected group state while"
- "creating group ID bitmap" FMTu64, *group_id);
- return 0;
- }
-
- group->group_id = *group_id;
- group->regions = regions;
-
- if (alias)
- group->alias = strdup(alias);
- else
- group->alias = NULL;
-
- /* force an update of the group tag stored in aux_data */
- if (!_stats_set_aux(dms, *group_id, dms->regions[*group_id].aux_data))
- return 0;
-
- return 1;
-}
-
-static int _stats_group_check_overlap(const struct dm_stats *dms,
- dm_bitset_t regions, int count)
-{
- struct dm_list ext_list = DM_LIST_HEAD_INIT(ext_list);
- struct _extent *ext, *tmp, *next, *map = NULL;
- size_t map_size = (dms->max_region + 1) * sizeof(*map);
- int i = 0, id, overlap, merged;
-
- map = dm_pool_alloc(dms->mem, map_size);
- if (!map) {
- log_error("Could not allocate memory for region map");
- return 0;
- }
-
- /* build a table of extents in order of region_id */
- for (id = dm_bit_get_first(regions); id >= 0;
- id = dm_bit_get_next(regions, id)) {
- dm_list_init(&map[i].list);
- map[i].id = id;
- map[i].start = dms->regions[id].start;
- map[i].len = dms->regions[id].len;
- i++;
- }
-
- /* A single region cannot overlap itself. */
- if (i == 1) {
- dm_pool_free(dms->mem, map);
- return 1;
- }
-
- /* sort by extent.start */
- qsort(map, count, sizeof(*map), _extent_start_compare);
-
- for (i = 0; i < count; i++)
- dm_list_add(&ext_list, &map[i].list);
-
- overlap = 0;
-merge:
- merged = 0;
- dm_list_iterate_items_safe(ext, tmp, &ext_list) {
- next = dm_list_item(dm_list_next(&ext_list, &ext->list),
- struct _extent);
- if (!next)
- continue;
-
- if (_extents_overlap(ext, next)) {
- log_warn("WARNING: region IDs " FMTu64 " and "
- FMTu64 " overlap. Some events will be "
- "counted twice.", ext->id, next->id);
- /* merge larger extent into smaller */
- if (_extent_end(ext) > _extent_end(next)) {
- next->id = ext->id;
- next->len = ext->len;
- }
- if (ext->start < next->start)
- next->start = ext->start;
- dm_list_del(&ext->list);
- overlap = merged = 1;
- }
- }
- /* continue until no merge candidates remain */
- if (merged)
- goto merge;
-
- dm_pool_free(dms->mem, map);
- return (overlap == 0);
-}
-
-static void _stats_copy_histogram_bounds(struct dm_histogram *to,
- struct dm_histogram *from)
-{
- int i;
-
- to->nr_bins = from->nr_bins;
-
- for (i = 0; i < to->nr_bins; i++)
- to->bins[i].upper = from->bins[i].upper;
-}
-
-/*
- * Compare histogram bounds h1 and h2, and return 1 if they match (i.e.
- * have the same number of bins and identical bin boundary values), or 0
- * otherwise.
- */
-static int _stats_check_histogram_bounds(struct dm_histogram *h1,
- struct dm_histogram *h2)
-{
- int i;
-
- if (!h1 || !h2)
- return 0;
-
- if (h1->nr_bins != h2->nr_bins)
- return 0;
-
- for (i = 0; i < h1->nr_bins; i++)
- if (h1->bins[i].upper != h2->bins[i].upper)
- return 0;
- return 1;
-}
-
-/*
- * Create a new group in stats handle dms from the group description
- * passed in group.
- */
-int dm_stats_create_group(struct dm_stats *dms, const char *members,
- const char *alias, uint64_t *group_id)
-{
- struct dm_histogram *check = NULL, *bounds;
- int i, count = 0, precise = 0;
- dm_bitset_t regions;
-
- if (!dms->regions || !dms->groups) {
- log_error("Could not create group: no regions found.");
- return 0;
- };
-
- if (!(regions = dm_bitset_parse_list(members, NULL, 0))) {
- log_error("Could not parse list: '%s'", members);
- return 0;
- }
-
- if (!(check = dm_pool_zalloc(dms->hist_mem, sizeof(*check)))) {
- log_error("Could not allocate memory for bounds check");
- goto bad;
- }
-
- /* too many bits? */
- if ((*regions - 1) > dms->max_region) {
- log_error("Invalid region ID: %d", *regions - 1);
- goto bad;
- }
-
- /*
- * Check that each region_id in the bitmap meets the group
- * constraints: present, not already grouped, and if any
- * histogram is present that they all have the same bounds.
- */
- for (i = dm_bit_get_first(regions); i >= 0;
- i = dm_bit_get_next(regions, i)) {
- if (!dm_stats_region_present(dms, i)) {
- log_error("Region ID %d does not exist", i);
- goto bad;
- }
- if (_stats_region_is_grouped(dms, i)) {
- log_error("Region ID %d already a member of group ID "
- FMTu64, i, dms->regions[i].group_id);
- goto bad;
- }
- if (dms->regions[i].timescale == 1)
- precise++;
-
- /* check for matching histogram bounds */
- bounds = dms->regions[i].bounds;
- if (bounds && !check->nr_bins)
- _stats_copy_histogram_bounds(check, bounds);
- else if (bounds) {
- if (!_stats_check_histogram_bounds(check, bounds)) {
- log_error("All region histogram bounds "
- "must match exactly");
- goto bad;
- }
- }
- count++;
- }
-
- if (precise && (precise != count))
- log_warn("WARNING: Grouping regions with different clock resolution: "
- "precision may be lost.");
-
- if (!_stats_group_check_overlap(dms, regions, count))
- log_very_verbose("Creating group with overlapping regions.");
-
- if (!_stats_create_group(dms, regions, alias, group_id))
- goto bad;
-
- dm_pool_free(dms->hist_mem, check);
- return 1;
-
-bad:
- dm_pool_free(dms->hist_mem, check);
- dm_bitset_destroy(regions);
- return 0;
-}
-
-/*
- * Remove the specified group_id.
- */
-int dm_stats_delete_group(struct dm_stats *dms, uint64_t group_id,
- int remove_regions)
-{
- struct dm_stats_region *leader;
- dm_bitset_t regions;
- uint64_t i;
-
- if (group_id > dms->max_region) {
- log_error("Invalid group ID: " FMTu64, group_id);
- return 0;
- }
-
- if (!_stats_group_id_present(dms, group_id)) {
- log_error("Group ID " FMTu64 " does not exist", group_id);
- return 0;
- }
-
- regions = dms->groups[group_id].regions;
- leader = &dms->regions[group_id];
-
- /* delete all but the group leader */
- for (i = (*regions - 1); i > leader->region_id; i--) {
- if (dm_bit(regions, i)) {
- dm_bit_clear(regions, i);
- if (remove_regions && !dm_stats_delete_region(dms, i))
- log_warn("WARNING: Failed to delete region "
- FMTu64 " on %s.", i, dms->name);
- }
- }
-
- /* clear group and mark as not present */
- _stats_clear_group_regions(dms, group_id);
- _stats_group_destroy(&dms->groups[group_id]);
-
- /* delete leader or clear aux_data */
- if (remove_regions)
- return dm_stats_delete_region(dms, group_id);
- else if (!_stats_set_aux(dms, group_id, leader->aux_data))
- return 0;
-
- return 1;
-}
-
-uint64_t dm_stats_get_group_id(const struct dm_stats *dms, uint64_t region_id)
-{
- region_id = (region_id == DM_STATS_REGION_CURRENT)
- ? dms->cur_region : region_id;
-
- if (region_id & DM_STATS_WALK_GROUP) {
- if (region_id == DM_STATS_WALK_GROUP)
- return dms->cur_group;
- else
- return region_id & ~DM_STATS_WALK_GROUP;
- }
-
- if (region_id & DM_STATS_WALK_REGION)
- region_id &= ~DM_STATS_WALK_REGION;
-
- return dms->regions[region_id].group_id;
-}
-
-int dm_stats_get_group_descriptor(const struct dm_stats *dms,
- uint64_t group_id, char **buf)
-{
- dm_bitset_t regions = dms->groups[group_id].regions;
- size_t buflen;
-
- buflen = _stats_group_tag_len(dms, regions);
-
- *buf = dm_pool_alloc(dms->mem, buflen);
- if (!*buf) {
- log_error("Could not allocate memory for regions string");
- return 0;
- }
-
- if (!_stats_group_tag_fill(dms, regions, *buf, buflen))
- return 0;
-
- return 1;
-}
-
-#ifdef HAVE_LINUX_FIEMAP_H
-/*
- * Resize the group bitmap corresponding to group_id so that it can
- * contain at least num_regions members.
- */
-static int _stats_resize_group(struct dm_stats_group *group,
- uint64_t num_regions)
-{
- uint64_t last_bit = dm_bit_get_last(group->regions);
- dm_bitset_t new, old;
-
- if (last_bit >= num_regions) {
- log_error("Cannot resize group bitmap to " FMTu64
- " with bit " FMTu64 " set.", num_regions, last_bit);
- return 0;
- }
-
- log_very_verbose("Resizing group bitmap from " FMTu32 " to " FMTu64
- " (last_bit: " FMTu64 ").", group->regions[0],
- num_regions, last_bit);
-
- new = dm_bitset_create(NULL, (unsigned) num_regions);
- if (!new) {
- log_error("Could not allocate memory for new group bitmap.");
- return 0;
- }
-
- old = group->regions;
- dm_bit_copy(new, old);
- group->regions = new;
- dm_bitset_destroy(old);
- return 1;
-}
-
-/*
- * Group a table of region_ids corresponding to the extents of a file.
- */
-static int _stats_group_file_regions(struct dm_stats *dms, uint64_t *region_ids,
- uint64_t count, const char *alias)
-{
- dm_bitset_t regions = dm_bitset_create(NULL, dms->nr_regions);
- uint64_t i, group_id = DM_STATS_GROUP_NOT_PRESENT;
- char *members = NULL;
- size_t buflen;
-
- if (!regions) {
- log_error("Cannot map file: failed to allocate group bitmap.");
- return 0;
- }
-
- for (i = 0; i < count; i++)
- dm_bit_set(regions, region_ids[i]);
-
- buflen = _stats_group_tag_len(dms, regions);
- members = malloc(buflen);
-
- if (!members) {
- log_error("Cannot map file: failed to allocate group "
- "descriptor.");
- dm_bitset_destroy(regions);
- return 0;
- }
-
- if (!_stats_group_tag_fill(dms, regions, members, buflen))
- goto bad;
-
- /*
- * overlaps should not be possible: overlapping file extents
- * returned by FIEMAP imply a kernel bug or a corrupt fs.
- */
- if (!_stats_group_check_overlap(dms, regions, count))
- log_very_verbose("Creating group with overlapping regions.");
-
- if (!_stats_create_group(dms, regions, alias, &group_id))
- goto bad;
-
- free(members);
- return 1;
-bad:
- dm_bitset_destroy(regions);
- free(members);
- return 0;
-}
-
-static int _stats_add_file_extent(int fd, struct dm_pool *mem, uint64_t id,
- struct fiemap_extent *fm_ext)
-{
- struct _extent extent;
-
- /* final address of list is unknown */
- memset(&extent.list, 0, sizeof(extent.list));
-
- /* convert bytes to dm (512b) sectors */
- extent.start = fm_ext->fe_physical >> SECTOR_SHIFT;
- extent.len = fm_ext->fe_length >> SECTOR_SHIFT;
- extent.id = id;
-
- log_very_verbose("Extent " FMTu64 " on fd %d at " FMTu64 "+"
- FMTu64, extent.id, fd, extent.start, extent.len);
-
- if (!dm_pool_grow_object(mem, &extent,
- sizeof(extent))) {
- log_error("Cannot map file: failed to grow extent map.");
- return 0;
- }
- return 1;
-}
-
-/* test for the boundary of an extent */
-#define ext_boundary(ext, exp) \
-((ext).fe_logical != 0) && \
-((ext).fe_physical != (exp))
-
-/*
- * Copy fields from fiemap_extent 'from' to the fiemap_extent
- * pointed to by 'to'.
- */
-#define ext_copy(to, from) \
-do { \
- *(to) = *(from); \
-} while (0)
-
-static uint64_t _stats_map_extents(int fd, struct dm_pool *mem,
- struct fiemap *fiemap,
- struct fiemap_extent *fm_ext,
- struct fiemap_extent *fm_last,
- struct fiemap_extent *fm_pending,
- uint64_t next_extent,
- int *eof)
-{
- uint64_t expected = 0, nr_extents = next_extent;
- unsigned int i;
-
- /*
- * Loop over the returned extents adding the fm_pending extent
- * to the table of extents each time a discontinuity (or eof)
- * is detected.
- *
- * We use a pointer to fm_pending in the caller since it is
- * possible that logical extents comprising a single physical
- * extent are returned by successive FIEMAP calls.
- */
- for (i = 0; i < fiemap->fm_mapped_extents; i++) {
- expected = fm_last->fe_physical + fm_last->fe_length;
-
- if (fm_ext[i].fe_flags & FIEMAP_EXTENT_LAST)
- *eof = 1;
-
- /* cannot map extents that are not yet allocated. */
- if (fm_ext[i].fe_flags
- & (FIEMAP_EXTENT_UNKNOWN | FIEMAP_EXTENT_DELALLOC))
- continue;
-
- /*
- * Begin a new extent if the current physical address differs
- * from the expected address yielded by fm_last.fe_physical +
- * fm_last.fe_length.
- *
- * A logical discontinuity is seen at the start of the file if
- * unwritten space exists before the first extent: do not add
- * any extent record until we have accumulated a non-zero length
- * in fm_pending.
- */
- if (fm_pending->fe_length &&
- ext_boundary(fm_ext[i], expected)) {
- if (!_stats_add_file_extent(fd, mem, nr_extents,
- fm_pending))
- goto_bad;
- nr_extents++;
- /* Begin a new pending extent. */
- ext_copy(fm_pending, fm_ext + i);
- } else {
- expected = 0;
- /* Begin a new pending extent for extent 0. If there is
- * a hole at the start of the file, the first allocated
- * extent will have a non-zero fe_logical. Detect this
- * case by testing fm_pending->fe_length: if no length
- * has been accumulated we are handling the first
- * physical extent of the file.
- */
- if (!fm_pending->fe_length || fm_ext[i].fe_logical == 0)
- ext_copy(fm_pending, fm_ext + i);
- else
- /* accumulate this logical extent's length */
- fm_pending->fe_length += fm_ext[i].fe_length;
- }
- *fm_last = fm_ext[i];
- }
-
- /*
- * If the file only has a single extent, no boundary is ever
- * detected to trigger addition of the first extent.
- */
- if (*eof || (fm_ext[i - 1].fe_logical == 0)) {
- _stats_add_file_extent(fd, mem, nr_extents, fm_pending);
- nr_extents++;
- }
-
- fiemap->fm_start = (fm_ext[i - 1].fe_logical +
- fm_ext[i - 1].fe_length);
-
- /* return the number of extents found in this call. */
- return nr_extents - next_extent;
-bad:
- /* signal mapping error to caller */
- *eof = -1;
- return 0;
-}
-
-/*
- * Read the extents of an open file descriptor into a table of struct _extent.
- *
- * Based on e2fsprogs/misc/filefrag.c::filefrag_fiemap().
- *
- * Copyright 2003 by Theodore Ts'o.
- *
- */
-static struct _extent *_stats_get_extents_for_file(struct dm_pool *mem, int fd,
- uint64_t *count)
-{
- struct fiemap_extent fm_last = {0}, fm_pending = {0}, *fm_ext = NULL;
- struct fiemap *fiemap = NULL;
- int eof = 0, nr_extents = 0;
- struct _extent *extents;
- unsigned long flags = 0;
- uint64_t *buf;
-
- /* grow temporary extent table in the pool */
- if (!dm_pool_begin_object(mem, sizeof(*extents)))
- return NULL;
-
- buf = zalloc(STATS_FIE_BUF_LEN);
- if (!buf) {
- log_error("Could not allocate memory for FIEMAP buffer.");
- goto bad;
- }
-
- /* initialise pointers into the ioctl buffer. */
- fiemap = (struct fiemap *) buf;
- fm_ext = &fiemap->fm_extents[0];
-
- /* space available per ioctl */
- *count = (STATS_FIE_BUF_LEN - sizeof(*fiemap))
- / sizeof(struct fiemap_extent);
-
- flags = FIEMAP_FLAG_SYNC;
-
- do {
- /* start of ioctl loop - zero size and set count to bufsize */
- fiemap->fm_length = ~0ULL;
- fiemap->fm_flags = flags;
- fiemap->fm_extent_count = *count;
-
- /* get count-sized chunk of extents */
- if (ioctl(fd, FS_IOC_FIEMAP, (unsigned long) fiemap) < 0) {
- if (errno == EBADR)
- log_err_once("FIEMAP failed with unknown "
- "flags %x.", fiemap->fm_flags);
- goto bad;
- }
-
- /* If 0 extents are returned, more ioctls are not needed */
- if (fiemap->fm_mapped_extents == 0)
- break;
-
- nr_extents += _stats_map_extents(fd, mem, fiemap, fm_ext,
- &fm_last, &fm_pending,
- nr_extents, &eof);
-
- /* check for extent mapping error */
- if (eof < 0)
- goto bad;
-
- } while (eof == 0);
-
- if (!nr_extents) {
- log_error("Cannot map file: no allocated extents.");
- goto bad;
- }
-
- /* return total number of extents */
- *count = nr_extents;
- extents = dm_pool_end_object(mem);
-
- /* free FIEMAP buffer. */
- free(buf);
-
- return extents;
-
-bad:
- *count = 0;
- dm_pool_abandon_object(mem);
- free(buf);
- return NULL;
-}
-
-#define MATCH_EXTENT(e, s, l) \
-(((e).start == (s)) && ((e).len == (l)))
-
-static struct _extent *_find_extent(uint64_t nr_extents, struct _extent *extents,
- uint64_t start, uint64_t len)
-{
- size_t i;
- for (i = 0; i < nr_extents; i++)
- if (MATCH_EXTENT(extents[i], start, len))
- return extents + i;
- return NULL;
-}
-
-/*
- * Clean up a table of region_id values that were created during a
- * failed dm_stats_create_regions_from_fd, or dm_stats_update_regions_from_fd
- * operation.
- */
-static void _stats_cleanup_region_ids(struct dm_stats *dms, uint64_t *regions,
- uint64_t nr_regions)
-{
- uint64_t i;
-
- for (i = 0; i < nr_regions; i++)
- if (!_stats_delete_region(dms, regions[i]))
- log_error("Could not delete region " FMTu64 ".", i);
-}
-
-/*
- * First update pass: prune no-longer-allocated extents from the group
- * and build a table of the remaining extents so that their creation
- * can be skipped in the second pass.
- */
-static int _stats_unmap_regions(struct dm_stats *dms, uint64_t group_id,
- struct dm_pool *mem, struct _extent *extents,
- struct _extent **old_extents, uint64_t *count,
- int *regroup)
-{
- struct dm_stats_region *region = NULL;
- struct dm_stats_group *group = NULL;
- uint64_t nr_kept, nr_old;
- struct _extent ext;
- int64_t i;
-
- group = &dms->groups[group_id];
-
- log_very_verbose("Checking for changed file extents in group ID "
- FMTu64, group_id);
-
- if (!dm_pool_begin_object(mem, sizeof(**old_extents))) {
- log_error("Could not allocate extent table.");
- return 0;
- }
-
- nr_kept = nr_old = 0; /* counts of old and retained extents */
-
- /*
- * First pass: delete de-allocated extents and set regroup=1 if
- * deleting the current group leader.
- */
- i = dm_bit_get_last(group->regions);
- for (; i >= 0; i = dm_bit_get_prev(group->regions, i)) {
- region = &dms->regions[i];
- nr_old++;
-
- if (extents && _find_extent(*count, extents,
- region->start, region->len)) {
- ext.start = region->start;
- ext.len = region->len;
- ext.id = i;
- nr_kept++;
-
- if (!dm_pool_grow_object(mem, &ext, sizeof(ext)))
- goto out;
-
- log_very_verbose("Kept region " FMTu64, i);
- } else {
-
- if (i == group_id)
- *regroup = 1;
-
- if (!_stats_delete_region(dms, i)) {
- log_error("Could not remove region ID " FMTu64,
- i);
- goto out;
- }
-
- log_very_verbose("Deleted region " FMTu64, i);
- }
- }
-
- *old_extents = dm_pool_end_object(mem);
- if (!*old_extents) {
- log_error("Could not finalize region extent table.");
- goto out;
- }
- log_very_verbose("Kept " FMTd64 " of " FMTd64 " old extents",
- nr_kept, nr_old);
- log_very_verbose("Found " FMTu64 " new extents",
- *count - nr_kept);
-
- return (int) nr_kept;
-out:
- dm_pool_abandon_object(mem);
- return -1;
-}
-
-/*
- * Create or update a set of regions representing the extents of a file
- * and return a table of uint64_t region_id values. The number of regions
- * created is returned in the memory pointed to by count (which must be
- * non-NULL).
- *
- * If group_id is not equal to DM_STATS_GROUP_NOT_PRESENT, it is assumed
- * that group_id corresponds to a group containing existing regions that
- * were mapped to this file at an earlier time: regions will be added or
- * removed to reflect the current status of the file.
- */
-static uint64_t *_stats_map_file_regions(struct dm_stats *dms, int fd,
- struct dm_histogram *bounds,
- int precise, uint64_t group_id,
- uint64_t *count, int *regroup)
-{
- struct _extent *extents = NULL, *old_extents = NULL;
- uint64_t *regions = NULL, fail_region, i, num_bits;
- struct dm_stats_group *group = NULL;
- struct dm_pool *extent_mem = NULL;
- struct _extent *old_ext;
- char *hist_arg = NULL;
- struct statfs fsbuf;
- int64_t nr_kept = 0;
- struct stat buf;
- int update;
-
- *count = 0;
- update = _stats_group_id_present(dms, group_id);
-
-#ifdef BTRFS_SUPER_MAGIC
- if (fstatfs(fd, &fsbuf)) {
- log_error("fstatfs failed for fd %d", fd);
- return 0;
- }
-
- if (fsbuf.f_type == BTRFS_SUPER_MAGIC) {
- log_error("Cannot map file: btrfs does not provide "
- "physical FIEMAP extent data.");
- return 0;
- }
-#endif
-
- if (fstat(fd, &buf)) {
- log_error("fstat failed for fd %d", fd);
- return 0;
- }
-
- if (!(buf.st_mode & S_IFREG)) {
- log_error("Not a regular file");
- return 0;
- }
-
- if (!dm_is_dm_major(major(buf.st_dev))) {
- log_error("Cannot map file: not a device-mapper device.");
- return 0;
- }
-
- /*
- * If regroup is set here, we are creating a new filemap: otherwise
- * we are updating a group with a valid group identifier in group_id.
- */
- if (update)
- log_very_verbose("Updating extents from fd %d with group ID "
- FMTu64 " on (%d:%d)", fd, group_id,
- major(buf.st_dev), minor(buf.st_dev));
- else
- log_very_verbose("Mapping extents from fd %d on (%d:%d)",
- fd, major(buf.st_dev), minor(buf.st_dev));
-
- /* Use a temporary, private pool for the extent table. This avoids
- * hijacking the dms->mem (region table) pool which would lead to
- * interleaving temporary allocations with dm_stats_list() data,
- * causing complications in the error path.
- */
- if (!(extent_mem = dm_pool_create("extents", sizeof(*extents))))
- return_NULL;
-
- if (!(extents = _stats_get_extents_for_file(extent_mem, fd, count))) {
- log_very_verbose("No extents found in fd %d", fd);
- if (!update)
- goto out;
- }
-
- if (update) {
- group = &dms->groups[group_id];
- if ((nr_kept = _stats_unmap_regions(dms, group_id, extent_mem,
- extents, &old_extents,
- count, regroup)) < 0)
- goto_out;
- }
-
- if (bounds)
- if (!(hist_arg = _build_histogram_arg(bounds, &precise)))
- goto_out;
-
- /* make space for end-of-table marker */
- if (!(regions = malloc((1 + *count) * sizeof(*regions)))) {
- log_error("Could not allocate memory for region IDs.");
- goto_out;
- }
-
- /*
- * Second pass (first for non-update case): create regions for
- * all extents not retained from the prior mapping, and insert
- * retained regions into the table of region_id values.
- *
- * If a regroup is not scheduled, set group bits for newly
- * created regions in the group leader bitmap.
- */
- for (i = 0; i < *count; i++) {
- if (update) {
- if ((old_ext = _find_extent((uint64_t) nr_kept,
- old_extents,
- extents[i].start,
- extents[i].len))) {
- regions[i] = old_ext->id;
- continue;
- }
- }
- if (!_stats_create_region(dms, regions + i, extents[i].start,
- extents[i].len, -1, precise, hist_arg,
- dms->program_id, "")) {
- log_error("Failed to create region " FMTu64 " of "
- FMTu64 " at " FMTu64 ".", i, *count,
- extents[i].start);
- goto out_remove;
- }
-
- log_very_verbose("Created new region mapping " FMTu64 "+" FMTu64
- " with region ID " FMTu64, extents[i].start,
- extents[i].len, regions[i]);
-
- if (!*regroup && update) {
- /* expand group bitmap */
- if (regions[i] > (group->regions[0] - 1)) {
- num_bits = regions[i] + *count;
- if (!_stats_resize_group(group, num_bits)) {
- log_error("Failed to resize group "
- "bitmap.");
- goto out_remove;
- }
- }
- dm_bit_set(group->regions, regions[i]);
- }
-
- }
- regions[*count] = DM_STATS_REGION_NOT_PRESENT;
-
- /* Update group leader aux_data for new group members. */
- if (!*regroup && update)
- if (!_stats_set_aux(dms, group_id,
- dms->regions[group_id].aux_data))
- log_error("Failed to update group aux_data.");
-
- if (bounds)
- free(hist_arg);
-
- /* the extent table will be empty if the file has been truncated. */
- if (extents)
- dm_pool_free(extent_mem, extents);
-
- dm_pool_destroy(extent_mem);
-
- return regions;
-
-out_remove:
- /* New region creation may begin to fail part-way through creating
- * a set of file mapped regions: in this case we need to roll back
- * the regions that were already created and return the handle to
- * a consistent state. A listed handle is required for this: use a
- * single list operation and call _stats_delete_region() directly
- * to avoid a @stats_list ioctl and list parsing for each region.
- */
- if (!dm_stats_list(dms, NULL))
- goto out;
-
- fail_region = i;
- _stats_cleanup_region_ids(dms, regions, fail_region);
- *count = 0;
-
-out:
- dm_pool_destroy(extent_mem);
- free(hist_arg);
- free(regions);
- return NULL;
-}
-
-uint64_t *dm_stats_create_regions_from_fd(struct dm_stats *dms, int fd,
- int group, int precise,
- struct dm_histogram *bounds,
- const char *alias)
-{
- uint64_t *regions, count;
- int regroup = 1;
-
- if (alias && !group) {
- log_error("Cannot set alias without grouping regions.");
- return NULL;
- }
-
- if (!(regions = _stats_map_file_regions(dms, fd, bounds, precise,
- DM_STATS_GROUP_NOT_PRESENT,
- &count, ®roup)))
- return NULL;
-
- if (!group)
- return regions;
-
- /* refresh handle */
- if (!dm_stats_list(dms, NULL))
- goto_out;
-
- if (!_stats_group_file_regions(dms, regions, count, alias))
- goto_out;
-
- return regions;
-out:
- _stats_cleanup_region_ids(dms, regions, count);
- free(regions);
- return NULL;
-}
-
-uint64_t *dm_stats_update_regions_from_fd(struct dm_stats *dms, int fd,
- uint64_t group_id)
-{
- struct dm_histogram *bounds = NULL;
- int nr_bins, precise, regroup;
- uint64_t *regions, count = 0;
- const char *alias = NULL;
-
- if (!dms->regions || !dm_stats_group_present(dms, group_id)) {
- if (!dm_stats_list(dms, dms->program_id)) {
- log_error("Could not obtain region list while "
- "updating group " FMTu64 ".", group_id);
- return NULL;
- }
- }
-
- if (!dm_stats_group_present(dms, group_id)) {
- log_error("Group ID " FMTu64 " does not exist.", group_id);
- return NULL;
- }
-
- /*
- * If the extent corresponding to the group leader's region has been
- * deallocated, _stats_map_file_regions() will remove the region and
- * the group. In this case, regroup will be set by the call and the
- * group will be re-created using saved values.
- */
- regroup = 0;
-
- /*
- * A copy of the alias is needed to re-create the group when regroup=1.
- */
- if (dms->groups[group_id].alias) {
- alias = strdup(dms->groups[group_id].alias);
- if (!alias) {
- log_error("Failed to allocate group alias string.");
- return NULL;
- }
- }
-
- if (dms->regions[group_id].bounds) {
- /*
- * A copy of the histogram bounds must be passed to
- * _stats_map_file_regions() to be used when creating new
- * regions: it is not safe to use the copy in the current group
- * leader since it may be destroyed during the first group
- * update pass.
- */
- nr_bins = dms->regions[group_id].bounds->nr_bins;
- bounds = _alloc_dm_histogram(nr_bins);
- if (!bounds) {
- log_error("Could not allocate memory for group "
- "histogram bounds.");
- goto out;
- }
- _stats_copy_histogram_bounds(bounds,
- dms->regions[group_id].bounds);
- }
-
- precise = (dms->regions[group_id].timescale == 1);
-
- regions = _stats_map_file_regions(dms, fd, bounds, precise,
- group_id, &count, ®roup);
-
- if (!regions)
- goto bad;
-
- if (!dm_stats_list(dms, NULL))
- goto bad;
-
- /* regroup if there are regions to group */
- if (regroup && (*regions != DM_STATS_REGION_NOT_PRESENT))
- if (!_stats_group_file_regions(dms, regions, count, alias))
- goto bad;
-
- free(bounds);
- free((char *) alias);
- return regions;
-bad:
- _stats_cleanup_region_ids(dms, regions, count);
- free(bounds);
- free(regions);
-out:
- free((char *) alias);
- return NULL;
-}
-#else /* !HAVE_LINUX_FIEMAP */
-uint64_t *dm_stats_create_regions_from_fd(struct dm_stats *dms, int fd,
- int group, int precise,
- struct dm_histogram *bounds,
- const char *alias)
-{
- log_error("File mapping requires FIEMAP ioctl support.");
- return 0;
-}
-
-uint64_t *dm_stats_update_regions_from_fd(struct dm_stats *dms, int fd,
- uint64_t group_id)
-{
- log_error("File mapping requires FIEMAP ioctl support.");
- return 0;
-}
-#endif /* HAVE_LINUX_FIEMAP */
-
-#ifdef DMFILEMAPD
-static const char *_filemapd_mode_names[] = {
- "inode",
- "path",
- NULL
-};
-
-dm_filemapd_mode_t dm_filemapd_mode_from_string(const char *mode_str)
-{
- dm_filemapd_mode_t mode = DM_FILEMAPD_FOLLOW_INODE;
- const char **mode_name;
-
- if (mode_str) {
- for (mode_name = _filemapd_mode_names; *mode_name; mode_name++)
- if (!strcmp(*mode_name, mode_str))
- break;
- if (*mode_name)
- mode = DM_FILEMAPD_FOLLOW_INODE
- + (mode_name - _filemapd_mode_names);
- else {
- log_error("Could not parse dmfilemapd mode: %s",
- mode_str);
- return DM_FILEMAPD_FOLLOW_NONE;
- }
- }
- return mode;
-}
-
-#define DM_FILEMAPD "dmfilemapd"
-#define NR_FILEMAPD_ARGS 7 /* includes argv[0] */
-/*
- * Start dmfilemapd to monitor the specified file descriptor, and to
- * update the group given by 'group_id' when the file's allocation
- * changes.
- *
- * usage: dmfilemapd <fd> <group_id> <mode> [<foreground>[<log_level>]]
- */
-int dm_stats_start_filemapd(int fd, uint64_t group_id, const char *path,
- dm_filemapd_mode_t mode, unsigned foreground,
- unsigned verbose)
-{
- char fd_str[8], group_str[8], fg_str[2], verb_str[2];
- const char *mode_str = _filemapd_mode_names[mode];
- char *args[NR_FILEMAPD_ARGS + 1];
- pid_t pid = 0;
- int argc = 0;
-
- if (fd < 0) {
- log_error("dmfilemapd file descriptor must be "
- "non-negative: %d", fd);
- return 0;
- }
-
- if (path[0] != '/') {
- log_error("Path argument must specify an absolute path.");
- return 0;
- }
-
- if (mode > DM_FILEMAPD_FOLLOW_PATH) {
- log_error("Invalid dmfilemapd mode argument: "
- "Must be DM_FILEMAPD_FOLLOW_INODE or "
- "DM_FILEMAPD_FOLLOW_PATH");
- return 0;
- }
-
- if (foreground > 1) {
- log_error("Invalid dmfilemapd foreground argument. "
- "Must be 0 or 1: %d.", foreground);
- return 0;
- }
-
- if (verbose > 3) {
- log_error("Invalid dmfilemapd verbose argument. "
- "Must be 0..3: %d.", verbose);
- return 0;
- }
-
- /* set argv[0] */
- args[argc++] = (char *) DM_FILEMAPD;
-
- /* set <fd> */
- if ((dm_snprintf(fd_str, sizeof(fd_str), "%d", fd)) < 0) {
- log_error("Could not format fd argument.");
- return 0;
- }
- args[argc++] = fd_str;
-
- /* set <group_id> */
- if ((dm_snprintf(group_str, sizeof(group_str), FMTu64, group_id)) < 0) {
- log_error("Could not format group_id argument.");
- return 0;
- }
- args[argc++] = group_str;
-
- /* set <path> */
- args[argc++] = (char *) path;
-
- /* set <mode> */
- args[argc++] = (char *) mode_str;
-
- /* set <foreground> */
- if ((dm_snprintf(fg_str, sizeof(fg_str), "%u", foreground)) < 0) {
- log_error("Could not format foreground argument.");
- return 0;
- }
- args[argc++] = fg_str;
-
- /* set <verbose> */
- if ((dm_snprintf(verb_str, sizeof(verb_str), "%u", verbose)) < 0) {
- log_error("Could not format verbose argument.");
- return 0;
- }
- args[argc++] = verb_str;
-
- /* terminate args[argc] */
- args[argc] = NULL;
-
- log_very_verbose("Spawning daemon as '%s %d " FMTu64 " %s %s %u %u'",
- *args, fd, group_id, path, mode_str,
- foreground, verbose);
-
- if (!foreground && ((pid = fork()) < 0)) {
- log_error("Failed to fork dmfilemapd process.");
- return 0;
- }
-
- if (pid > 0) {
- log_very_verbose("Forked dmfilemapd process as pid %d", pid);
- return 1;
- }
-
- execvp(args[0], args);
- log_sys_error("execvp", args[0]);
- if (!foreground)
- _exit(127);
- return 0;
-}
-# else /* !DMFILEMAPD */
-dm_filemapd_mode_t dm_filemapd_mode_from_string(const char *mode_str)
-{
- return 0;
-};
-
-int dm_stats_start_filemapd(int fd, uint64_t group_id, const char *path,
- dm_filemapd_mode_t mode, unsigned foreground,
- unsigned verbose)
-{
- log_error("dmfilemapd support disabled.");
- return 0;
-}
-#endif /* DMFILEMAPD */
-
-/*
- * Backward compatible dm_stats_create_region() implementations.
- *
- * Keep these at the end of the file to avoid adding clutter around the
- * current dm_stats_create_region() version.
- */
-
-#if defined(__GNUC__)
-int dm_stats_create_region_v1_02_106(struct dm_stats *dms, uint64_t *region_id,
- uint64_t start, uint64_t len, int64_t step,
- int precise, const char *program_id,
- const char *aux_data);
-int dm_stats_create_region_v1_02_106(struct dm_stats *dms, uint64_t *region_id,
- uint64_t start, uint64_t len, int64_t step,
- int precise, const char *program_id,
- const char *aux_data)
-{
- /* 1.02.106 lacks histogram argument. */
- return _stats_create_region(dms, region_id, start, len, step, precise,
- NULL, program_id, aux_data);
-}
-
-int dm_stats_create_region_v1_02_104(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);
-int dm_stats_create_region_v1_02_104(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)
-{
- /* 1.02.104 lacks histogram and precise arguments. */
- return _stats_create_region(dms, region_id, start, len, step, 0, NULL,
- program_id, aux_data);
-}
-#endif
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=e53cfc6a88d7acafde6ba…
Commit: e53cfc6a88d7acafde6ba3fa293bcf15b9a033bc
Parent: 9b79f0244a17c91d790e2114e1c912e5efe00a2f
Author: David Teigland <teigland(a)redhat.com>
AuthorDate: Wed Jun 13 15:30:28 2018 -0500
Committer: David Teigland <teigland(a)redhat.com>
CommitterDate: Wed Jun 13 15:30:28 2018 -0500
lvmlockd: update method for changing clustered VG
The previous method for forcibly changing a clustered VG
to a local VG involved using -cn and locking_type 0.
Since those options are deprecated, replace it with
the same command used for other forced lock type changes:
vgchange --locktype none --lockopt force.
---
lib/commands/toolcontext.h | 1 +
lib/metadata/metadata.c | 9 +++++++++
man/lvmlockd.8_main | 9 +++------
tools/vgchange.c | 1 +
4 files changed, 14 insertions(+), 6 deletions(-)
diff --git a/lib/commands/toolcontext.h b/lib/commands/toolcontext.h
index baecfbd..9849e5f 100644
--- a/lib/commands/toolcontext.h
+++ b/lib/commands/toolcontext.h
@@ -153,6 +153,7 @@ struct cmd_context {
unsigned include_shared_vgs:1; /* report/display cmds can reveal lockd VGs */
unsigned include_active_foreign_vgs:1; /* cmd should process foreign VGs with active LVs */
unsigned vg_read_print_access_error:1; /* print access errors from vg_read */
+ unsigned force_access_clustered:1;
unsigned lockd_gl_disable:1;
unsigned lockd_vg_disable:1;
unsigned lockd_lv_disable:1;
diff --git a/lib/metadata/metadata.c b/lib/metadata/metadata.c
index 40854de..113c87c 100644
--- a/lib/metadata/metadata.c
+++ b/lib/metadata/metadata.c
@@ -4689,6 +4689,15 @@ int vg_flag_write_locked(struct volume_group *vg)
static int _access_vg_clustered(struct cmd_context *cmd, const struct volume_group *vg)
{
if (vg_is_clustered(vg)) {
+ /*
+ * force_access_clustered is only set when forcibly
+ * converting a clustered vg to lock type none.
+ */
+ if (cmd->force_access_clustered) {
+ log_debug("Allowing forced access to clustered vg %s", vg->name);
+ return 1;
+ }
+
if (!cmd->ignore_clustered_vgs)
log_error("Skipping clustered volume group %s", vg->name);
else
diff --git a/man/lvmlockd.8_main b/man/lvmlockd.8_main
index cf4c52e..6411c01 100644
--- a/man/lvmlockd.8_main
+++ b/man/lvmlockd.8_main
@@ -814,8 +814,8 @@ dlm), first change it to a local VG, then to the new type.
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:
+First change the clvm/clustered VG to a local VG. Within a running clvm
+cluster, change a clustered VG to a local VG with the command:
vgchange -cn <vgname>
@@ -823,10 +823,7 @@ If the clvm cluster is no longer running on any nodes, then extra options
can be used to 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
+vgchange --lock-type none --lock-opt force <vgname>
After the VG is local, follow the steps described in "changing a local VG
to a lockd VG".
diff --git a/tools/vgchange.c b/tools/vgchange.c
index b427a81..891f22a 100644
--- a/tools/vgchange.c
+++ b/tools/vgchange.c
@@ -1039,6 +1039,7 @@ int vgchange_locktype_cmd(struct cmd_context *cmd, int argc, char **argv)
cmd->lockd_vg_disable = 1;
cmd->lockd_lv_disable = 1;
cmd->handles_missing_pvs = 1;
+ cmd->force_access_clustered = 1;
goto process;
}
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=fa00fce97c1eabef9f1ec…
Commit: fa00fce97c1eabef9f1ec88dd420660cd4d6f004
Parent: a163d5341a90c1c2f656b0d15c055bbd90333a60
Author: David Teigland <teigland(a)redhat.com>
AuthorDate: Tue Jun 12 14:35:27 2018 -0500
Committer: David Teigland <teigland(a)redhat.com>
CommitterDate: Wed Jun 13 14:37:16 2018 -0500
Remove systemd script for starting shared VG
Shared VGs will generally be started and activated by
the resource agent. Without the agent, this script doesn't
have a good way to know which LVs to activate.
---
scripts/lvm2_lvmlocking_systemd_red_hat.service.in | 24 --------------------
1 files changed, 0 insertions(+), 24 deletions(-)
diff --git a/scripts/lvm2_lvmlocking_systemd_red_hat.service.in b/scripts/lvm2_lvmlocking_systemd_red_hat.service.in
deleted file mode 100644
index 62d8177..0000000
--- a/scripts/lvm2_lvmlocking_systemd_red_hat.service.in
+++ /dev/null
@@ -1,24 +0,0 @@
-[Unit]
-Description=Availability of lockspaces in lvmlockd
-Documentation=man:lvmlockd(8)
-After=lvm2-lvmlockd.service sanlock.service dlm.service
-
-[Service]
-Type=oneshot
-RemainAfterExit=yes
-
-# start lockspaces and wait for them to finish starting
-ExecStart=@SBINDIR@/lvm vgchange --lock-start --lock-opt autowait
-
-# auto activate LVs in the newly started lockd VGs
-ExecStart=@SBINDIR@/lvm vgchange -aay -S 'locktype=sanlock || locktype=dlm'
-
-# deactivate LVs in lockd VGs
-ExecStop=@SBINDIR@/lvm vgchange -an -S 'locktype=sanlock || locktype=dlm'
-
-# stop lockspaces and wait for them to finish stopping
-ExecStop=@SBINDIR@/lvmlockctl --stop-lockspaces --wait 1
-
-[Install]
-WantedBy=multi-user.target
-
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=c4153a8dfca9defcea645…
Commit: c4153a8dfca9defcea645818dcd547388858c51e
Parent: 3b6b7f8f9be1841fc7d283203a1f3261e7e3622a
Author: David Teigland <teigland(a)redhat.com>
AuthorDate: Mon Jun 11 12:25:52 2018 -0500
Committer: David Teigland <teigland(a)redhat.com>
CommitterDate: Tue Jun 12 09:46:04 2018 -0500
Remove checking for locked VGs
A few places were calling a function to check if a
VG lock was held. The only place it was actually
needed is for pvcreate which wants to do its own
locking (and scanning) around process_each_pv.
The locking/scanning exceptions for pvcreate in
process_each_pv/vg_read can be enabled by just passing
a couple of flags instead of checking if the VG is
already locked. This also means that these special
cases won't be enabled unknowingly in other places
where they shouldn't be used.
---
lib/cache/lvmcache.c | 8 ---
lib/cache/lvmcache.h | 1 -
lib/metadata/metadata-exported.h | 2 +
lib/metadata/metadata.c | 18 ++-----
tools/toollib.c | 94 +++++++++++---------------------------
5 files changed, 34 insertions(+), 89 deletions(-)
diff --git a/lib/cache/lvmcache.c b/lib/cache/lvmcache.c
index 923fb2e..757ed88 100644
--- a/lib/cache/lvmcache.c
+++ b/lib/cache/lvmcache.c
@@ -142,14 +142,6 @@ void lvmcache_lock_vgname(const char *vgname, int read_only __attribute__((unuse
_vgs_locked++;
}
-int lvmcache_vgname_is_locked(const char *vgname)
-{
- if (!_lock_hash)
- return 0;
-
- return dm_hash_lookup(_lock_hash, is_orphan_vg(vgname) ? VG_ORPHANS : vgname) ? 1 : 0;
-}
-
void lvmcache_unlock_vgname(const char *vgname)
{
if (!dm_hash_lookup(_lock_hash, vgname))
diff --git a/lib/cache/lvmcache.h b/lib/cache/lvmcache.h
index 536f6fd..008f110 100644
--- a/lib/cache/lvmcache.h
+++ b/lib/cache/lvmcache.h
@@ -105,7 +105,6 @@ struct device *lvmcache_device_from_pvid(struct cmd_context *cmd, const struct i
const char *lvmcache_vgname_from_info(struct lvmcache_info *info);
const struct format_type *lvmcache_fmt_from_info(struct lvmcache_info *info);
int lvmcache_vgs_locked(void);
-int lvmcache_vgname_is_locked(const char *vgname);
void lvmcache_seed_infos_from_lvmetad(struct cmd_context *cmd);
diff --git a/lib/metadata/metadata-exported.h b/lib/metadata/metadata-exported.h
index bcd10a3..641bf49 100644
--- a/lib/metadata/metadata-exported.h
+++ b/lib/metadata/metadata-exported.h
@@ -178,6 +178,8 @@
#define READ_OK_NOTFOUND 0x00040000U
#define READ_WARN_INCONSISTENT 0x00080000U
#define READ_FOR_UPDATE 0x00100000U /* A meta-flag, useful with toollib for_each_* functions. */
+#define PROCESS_SKIP_SCAN 0x00200000U /* skip lvmcache_label_scan in process_each_pv */
+#define PROCESS_SKIP_ORPHAN_LOCK 0x00400000U /* skip lock_vol(VG_ORPHAN) in vg_read */
/* vg's "read_status" field */
#define FAILED_INCONSISTENT 0x00000001U
diff --git a/lib/metadata/metadata.c b/lib/metadata/metadata.c
index ff8f1a5..30b22c0 100644
--- a/lib/metadata/metadata.c
+++ b/lib/metadata/metadata.c
@@ -3049,12 +3049,6 @@ int vg_commit(struct volume_group *vg)
int cache_updated = 0;
struct pv_list *pvl;
- if (!lvmcache_vgname_is_locked(vg->name)) {
- log_error(INTERNAL_ERROR "Attempt to write new VG metadata "
- "without locking %s", vg->name);
- return cache_updated;
- }
-
cache_updated = _vg_commit_mdas(vg);
set_vg_notify(vg->cmd);
@@ -5032,7 +5026,7 @@ static struct volume_group *_vg_lock_and_read(struct cmd_context *cmd, const cha
uint32_t failure = 0;
uint32_t warn_flags = 0;
int is_shared = 0;
- int already_locked;
+ int skip_lock = is_orphan_vg(vg_name) && (read_flags & PROCESS_SKIP_ORPHAN_LOCK);
if ((read_flags & READ_ALLOW_INCONSISTENT) || (lock_flags != LCK_VG_WRITE))
consistent = 0;
@@ -5043,15 +5037,13 @@ static struct volume_group *_vg_lock_and_read(struct cmd_context *cmd, const cha
return NULL;
}
- already_locked = lvmcache_vgname_is_locked(vg_name);
-
- if (!already_locked &&
+ if (!skip_lock &&
!lock_vol(cmd, vg_name, lock_flags, NULL)) {
log_error("Can't get lock for %s", vg_name);
return _vg_make_handle(cmd, vg, FAILED_LOCKING);
}
- if (already_locked)
+ if (skip_lock)
log_very_verbose("Locking %s already done", vg_name);
if (is_orphan_vg(vg_name))
@@ -5119,13 +5111,13 @@ static struct volume_group *_vg_lock_and_read(struct cmd_context *cmd, const cha
goto_bad;
if (!(vg = _vg_make_handle(cmd, vg, failure)) || vg_read_error(vg))
- if (!already_locked)
+ if (!skip_lock)
unlock_vg(cmd, vg, vg_name);
return vg;
bad:
- if (!already_locked)
+ if (!skip_lock)
unlock_vg(cmd, vg, vg_name);
bad_no_unlock:
diff --git a/tools/toollib.c b/tools/toollib.c
index 7da1703..fb37d07 100644
--- a/tools/toollib.c
+++ b/tools/toollib.c
@@ -1926,7 +1926,6 @@ static int _process_vgnameid_list(struct cmd_context *cmd, uint32_t read_flags,
int skip;
int notfound;
int process_all = 0;
- int already_locked;
int do_report_ret_code = 1;
log_set_report_object_type(LOG_REPORT_OBJECT_TYPE_VG);
@@ -1969,8 +1968,6 @@ static int _process_vgnameid_list(struct cmd_context *cmd, uint32_t read_flags,
continue;
}
- already_locked = lvmcache_vgname_is_locked(vg_name);
-
vg = vg_read(cmd, vg_name, vg_uuid, read_flags, lockd_state);
if (_ignore_vg(vg, vg_name, arg_vgnames, read_flags, &skip, ¬found)) {
stack;
@@ -1998,7 +1995,7 @@ static int _process_vgnameid_list(struct cmd_context *cmd, uint32_t read_flags,
ret_max = ret;
}
- if (!vg_read_error(vg) && !already_locked)
+ if (!vg_read_error(vg))
unlock_vg(cmd, vg, vg_name);
endvg:
release_vg(vg);
@@ -3577,7 +3574,6 @@ static int _process_lv_vgnameid_list(struct cmd_context *cmd, uint32_t read_flag
int ret;
int skip;
int notfound;
- int already_locked;
int do_report_ret_code = 1;
log_set_report_object_type(LOG_REPORT_OBJECT_TYPE_VG);
@@ -3638,8 +3634,6 @@ static int _process_lv_vgnameid_list(struct cmd_context *cmd, uint32_t read_flag
continue;
}
- already_locked = lvmcache_vgname_is_locked(vg_name);
-
vg = vg_read(cmd, vg_name, vg_uuid, read_flags, lockd_state);
if (_ignore_vg(vg, vg_name, arg_vgnames, read_flags, &skip, ¬found)) {
stack;
@@ -3658,8 +3652,7 @@ static int _process_lv_vgnameid_list(struct cmd_context *cmd, uint32_t read_flag
if (ret > ret_max)
ret_max = ret;
- if (!already_locked)
- unlock_vg(cmd, vg, vg_name);
+ unlock_vg(cmd, vg, vg_name);
endvg:
release_vg(vg);
if (!lockd_vg(cmd, vg_name, "un", 0, &lockd_state))
@@ -4299,7 +4292,7 @@ static int _process_pvs_in_vgs(struct cmd_context *cmd, uint32_t read_flags,
int ret;
int skip;
int notfound;
- int already_locked;
+ int skip_lock;
int do_report_ret_code = 1;
log_set_report_object_type(LOG_REPORT_OBJECT_TYPE_VG);
@@ -4333,7 +4326,7 @@ static int _process_pvs_in_vgs(struct cmd_context *cmd, uint32_t read_flags,
log_debug("Processing PVs in VG %s", vg_name);
- already_locked = lvmcache_vgname_is_locked(vg_name);
+ skip_lock = is_orphan_vg(vg_name) && (read_flags & PROCESS_SKIP_ORPHAN_LOCK);
vg = vg_read(cmd, vg_name, vg_uuid, read_flags, lockd_state);
if (_ignore_vg(vg, vg_name, NULL, read_flags, &skip, ¬found)) {
@@ -4361,7 +4354,7 @@ static int _process_pvs_in_vgs(struct cmd_context *cmd, uint32_t read_flags,
if (ret > ret_max)
ret_max = ret;
- if (!skip && !already_locked)
+ if (!skip && !skip_lock)
unlock_vg(cmd, vg, vg->name);
endvg:
release_vg(vg);
@@ -4400,7 +4393,6 @@ int process_each_pv(struct cmd_context *cmd,
struct device_id_list *dil;
int process_all_pvs;
int process_all_devices;
- int orphans_locked;
int ret_max = ECMD_PROCESSED;
int ret;
@@ -4448,8 +4440,6 @@ int process_each_pv(struct cmd_context *cmd,
return ECMD_FAILED;
}
- orphans_locked = lvmcache_vgname_is_locked(VG_ORPHANS);
-
process_all_pvs = dm_list_empty(&arg_pvnames) && dm_list_empty(&arg_tags);
process_all_devices = process_all_pvs && (cmd->cname->flags & ENABLE_ALL_DEVS) && all_is_set;
@@ -4460,22 +4450,8 @@ int process_each_pv(struct cmd_context *cmd,
goto_out;
}
- /*
- * This full scan would be done by _get_all_devices() if
- * it were not done here first. It's called here first
- * so that get_vgnameids() will look at any new devices.
- * When orphans is already locked, these steps are done
- * before process_each_pv is called.
- */
- if (!trust_cache() && !orphans_locked) {
- lvmcache_destroy(cmd, 1, 0);
-
- /*
- * Scan all devices to populate lvmcache with initial
- * list of PVs and VGs.
- */
+ if (!(read_flags & PROCESS_SKIP_SCAN))
lvmcache_label_scan(cmd);
- }
if (!get_vgnameids(cmd, &all_vgnameids, only_this_vgname, 1)) {
ret_max = ret;
@@ -4546,11 +4522,9 @@ int process_each_pv(struct cmd_context *cmd,
ret_max = ret;
/*
- * If the orphans lock was held, there shouldn't be missed devices. If
- * there were, we cannot clear the cache while holding the orphans lock
- * anyway.
+ * If the orphans lock was held, there shouldn't be missed devices.
*/
- if (orphans_locked)
+ if (read_flags & PROCESS_SKIP_ORPHAN_LOCK)
goto skip_missed;
/*
@@ -4577,12 +4551,7 @@ int process_each_pv(struct cmd_context *cmd,
log_verbose("Some PVs were not found in first search, retrying.");
- lvmcache_destroy(cmd, 0, 0);
- if (!lvmcache_init(cmd)) {
- log_error("Failed to initalize lvm cache.");
- ret_max = ECMD_FAILED;
- goto out;
- }
+ lvmcache_label_scan(cmd);
lvmcache_seed_infos_from_lvmetad(cmd);
ret = _process_pvs_in_vgs(cmd, read_flags, &all_vgnameids, &all_devices,
@@ -5413,25 +5382,16 @@ int pvcreate_each_device(struct cmd_context *cmd,
dm_list_add(&pp->arg_devices, &pd->list);
}
- /*
- * Clear the cache before acquiring the orphan lock. (Clearing the
- * cache with locks held is an error.) We want the orphan lock
- * acquired before process_each_pv. If the orphan lock is not held
- * when process_each_pv is called, then process_each_pv clears the
- * cache.
- */
- lvmcache_destroy(cmd, 1, 0);
-
- /*
- * If no prompts require a user response, this orphan lock is held
- * throughout, and pvcreate_each_device() returns with it held so that
- * vgcreate/vgextend use the PVs created here to add to a VG.
- */
if (!lock_vol(cmd, VG_ORPHANS, LCK_VG_WRITE, NULL)) {
log_error("Can't get lock for orphan PVs.");
return 0;
}
+ /*
+ * Scan before calling process_each_pv so we can set up the PV args
+ * first. We can then skip the scan that would normally occur at the
+ * beginning of process_each_pv.
+ */
lvmcache_label_scan(cmd);
/*
@@ -5455,8 +5415,8 @@ int pvcreate_each_device(struct cmd_context *cmd,
* If it's added to arg_process but needs a prompt or force option, then
* a corresponding prompt entry is added to pp->prompts.
*/
- process_each_pv(cmd, 0, NULL, NULL, 1, 0, handle,
- pp->is_remove ? _pvremove_check_single : _pvcreate_check_single);
+ process_each_pv(cmd, 0, NULL, NULL, 1, PROCESS_SKIP_SCAN | PROCESS_SKIP_ORPHAN_LOCK,
+ handle, pp->is_remove ? _pvremove_check_single : _pvcreate_check_single);
/*
* A fatal error was found while checking.
@@ -5538,9 +5498,11 @@ int pvcreate_each_device(struct cmd_context *cmd,
goto do_command;
/*
- * Prompts require asking the user, so release the orphans lock, ask
- * the questions, reacquire the orphans lock, verify that the PVs were
- * not used during the questions, then do the create steps.
+ * Prompts require asking the user and make take some time, during
+ * which we don't want to block other commands. So, release the lock
+ * to prevent blocking other commands while we wait. After a response
+ * from the user, reacquire the lock, verify that the PVs were not used
+ * during the wait, then do the create steps.
*/
unlock_vg(cmd, NULL, VG_ORPHANS);
@@ -5575,14 +5537,11 @@ int pvcreate_each_device(struct cmd_context *cmd,
}
/*
- * Clear the cache, reacquire the orphans write lock, then check again
- * that the devices can still be used. If the second loop finds them
- * changed, or can't find them any more, then they aren't used.
- * Clear the cache here before locking orphans, since it won't be
- * done by process_each_pv with orphans already locked.
+ * Reacquire the lock that was released above before waiting, then
+ * check again that the devices can still be used. If the second loop
+ * finds them changed, or can't find them any more, then they aren't
+ * used.
*/
- lvmcache_destroy(cmd, 1, 0);
-
if (!lock_vol(cmd, VG_ORPHANS, LCK_VG_WRITE, NULL)) {
log_error("Can't get lock for orphan PVs.");
goto out;
@@ -5604,7 +5563,8 @@ int pvcreate_each_device(struct cmd_context *cmd,
*/
dm_list_splice(&pp->arg_confirm, &pp->arg_process);
- process_each_pv(cmd, 0, NULL, NULL, 1, 0, handle, _pv_confirm_single);
+ process_each_pv(cmd, 0, NULL, NULL, 1, PROCESS_SKIP_SCAN | PROCESS_SKIP_ORPHAN_LOCK,
+ handle, _pv_confirm_single);
dm_list_iterate_items(pd, &pp->arg_confirm)
log_error("Device %s %s.", pd->name, dev_cache_filtered_reason(pd->name));
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=5c2f7f083cc518b11e657…
Commit: 5c2f7f083cc518b11e657b294f42e38a688404e1
Parent: 2a307ce33c515268763c714275bd4a85bf9fd0fa
Author: Zdenek Kabelac <zkabelac(a)redhat.com>
AuthorDate: Fri Jun 8 22:16:00 2018 +0200
Committer: Zdenek Kabelac <zkabelac(a)redhat.com>
CommitterDate: Mon Jun 11 22:25:42 2018 +0200
build: make generate
---
conf/example.conf.in | 43 ++++---------------------------------------
man/lvchange.8_pregen | 14 ++++++++++++++
man/lvconvert.8_pregen | 14 ++++++++++++++
man/lvcreate.8_pregen | 14 ++++++++++++++
man/lvdisplay.8_pregen | 10 ++++++++++
man/lvextend.8_pregen | 14 ++++++++++++++
man/lvm-fullreport.8_pregen | 8 ++++----
man/lvm-lvpoll.8_pregen | 10 ++++++++++
man/lvmconfig.8_pregen | 10 ++++++++++
man/lvmdiskscan.8_pregen | 10 ++++++++++
man/lvreduce.8_pregen | 10 ++++++++++
man/lvremove.8_pregen | 10 ++++++++++
man/lvrename.8_pregen | 10 ++++++++++
man/lvresize.8_pregen | 14 ++++++++++++++
man/lvs.8_pregen | 8 ++++----
man/lvscan.8_pregen | 10 ++++++++++
man/pvchange.8_pregen | 10 ++++++++++
man/pvck.8_pregen | 10 ++++++++++
man/pvcreate.8_pregen | 10 ++++++++++
man/pvdisplay.8_pregen | 10 ++++++++++
man/pvmove.8_pregen | 10 ++++++++++
man/pvremove.8_pregen | 10 ++++++++++
man/pvresize.8_pregen | 10 ++++++++++
man/pvs.8_pregen | 8 ++++----
man/pvscan.8_pregen | 10 ++++++++++
man/vgcfgbackup.8_pregen | 10 ++++++++++
man/vgcfgrestore.8_pregen | 14 ++++++++++++++
man/vgchange.8_pregen | 29 ++++++++++++++---------------
man/vgck.8_pregen | 10 ++++++++++
man/vgconvert.8_pregen | 10 ++++++++++
man/vgcreate.8_pregen | 17 ++++++++++++-----
man/vgdisplay.8_pregen | 10 ++++++++++
man/vgexport.8_pregen | 10 ++++++++++
man/vgextend.8_pregen | 10 ++++++++++
man/vgimport.8_pregen | 10 ++++++++++
man/vgimportclone.8_pregen | 10 ++++++++++
man/vgmerge.8_pregen | 10 ++++++++++
man/vgmknodes.8_pregen | 10 ++++++++++
man/vgreduce.8_pregen | 14 ++++++++++++++
man/vgremove.8_pregen | 10 ++++++++++
man/vgrename.8_pregen | 10 ++++++++++
man/vgs.8_pregen | 8 ++++----
man/vgscan.8_pregen | 10 ++++++++++
man/vgsplit.8_pregen | 20 ++++++++++----------
44 files changed, 444 insertions(+), 85 deletions(-)
diff --git a/conf/example.conf.in b/conf/example.conf.in
index 4c62aad..97daab2 100644
--- a/conf/example.conf.in
+++ b/conf/example.conf.in
@@ -728,34 +728,7 @@ global {
etc = "@CONFDIR@"
# Configuration option global/locking_type.
- # Type of locking to use.
- #
- # Accepted values:
- # 0
- # Turns off locking. Warning: this risks metadata corruption if
- # commands run concurrently.
- # 1
- # LVM uses local file-based locking, the standard mode.
- # 2
- # LVM uses the external shared library locking_library.
- # 3
- # LVM uses built-in clustered locking with clvmd.
- # This is incompatible with lvmetad. If use_lvmetad is enabled,
- # LVM prints a warning and disables lvmetad use.
- # 4
- # LVM uses read-only locking which forbids any operations that
- # might change metadata.
- # 5
- # Offers dummy locking for tools that do not need any locks.
- # You should not need to set this directly; the tools will select
- # when to use it instead of the configured locking_type.
- # Do not use lvmetad or the kernel device-mapper driver with this
- # locking type. It is used by the --readonly option that offers
- # read-only access to Volume Group metadata that cannot be locked
- # safely because it belongs to an inaccessible domain and might be
- # in use, for example a virtual machine image or a disk that is
- # shared by a clustered machine.
- #
+ # This setting is no longer used.
locking_type = 1
# Configuration option global/wait_for_locks.
@@ -763,19 +736,11 @@ global {
wait_for_locks = 1
# Configuration option global/fallback_to_clustered_locking.
- # Attempt to use built-in cluster locking if locking_type 2 fails.
- # If using external locking (type 2) and initialisation fails, with
- # this enabled, an attempt will be made to use the built-in clustered
- # locking. Disable this if using a customised locking_library.
+ # This setting is no longer used.
fallback_to_clustered_locking = 1
# Configuration option global/fallback_to_local_locking.
- # Use locking_type 1 (local) if locking_type 2 or 3 fail.
- # If an attempt to initialise type 2 or type 3 locking failed, perhaps
- # because cluster components such as clvmd are not running, with this
- # enabled, an attempt will be made to use local file-based locking
- # (type 1). If this succeeds, only commands against local VGs will
- # proceed. VGs marked as clustered will be ignored.
+ # This setting is no longer used.
fallback_to_local_locking = 1
# Configuration option global/locking_dir.
@@ -799,7 +764,7 @@ global {
# This configuration option does not have a default value defined.
# Configuration option global/locking_library.
- # The external locking library to use for locking_type 2.
+ # This setting is no longer used.
# This configuration option has an automatic default value.
# locking_library = "liblvm2clusterlock.so"
diff --git a/man/lvchange.8_pregen b/man/lvchange.8_pregen
index 26a6297..8f9783a 100644
--- a/man/lvchange.8_pregen
+++ b/man/lvchange.8_pregen
@@ -133,6 +133,10 @@ lvchange - Change the attributes of logical volume(s)
.ad b
.br
.ad l
+ \fB--nolocking\fP
+.ad b
+.br
+.ad l
\fB--noudevsync\fP
.ad b
.br
@@ -576,6 +580,10 @@ Common options for lvm:
.ad b
.br
.ad l
+[ \fB--nolocking\fP ]
+.ad b
+.br
+.ad l
[ \fB--profile\fP \fIString\fP ]
.ad b
.br
@@ -876,6 +884,12 @@ See \fBdmeventd\fP(8) for more information.
.ad b
.HP
.ad l
+\fB--nolocking\fP
+.br
+Disable locking.
+.ad b
+.HP
+.ad l
\fB--noudevsync\fP
.br
Disables udev synchronisation. The process will not wait for notification
diff --git a/man/lvconvert.8_pregen b/man/lvconvert.8_pregen
index 91691a3..c9df4da 100644
--- a/man/lvconvert.8_pregen
+++ b/man/lvconvert.8_pregen
@@ -119,6 +119,10 @@ lvconvert - Change logical volume layout
.ad b
.br
.ad l
+ \fB--nolocking\fP
+.ad b
+.br
+.ad l
\fB--noudevsync\fP
.ad b
.br
@@ -903,6 +907,10 @@ Common options for lvm:
.ad b
.br
.ad l
+[ \fB--nolocking\fP ]
+.ad b
+.br
+.ad l
[ \fB--profile\fP \fIString\fP ]
.ad b
.br
@@ -1177,6 +1185,12 @@ generated, where # is a number generated by LVM.
.ad b
.HP
.ad l
+\fB--nolocking\fP
+.br
+Disable locking.
+.ad b
+.HP
+.ad l
\fB--noudevsync\fP
.br
Disables udev synchronisation. The process will not wait for notification
diff --git a/man/lvcreate.8_pregen b/man/lvcreate.8_pregen
index 9dcb33f..b6483b5 100644
--- a/man/lvcreate.8_pregen
+++ b/man/lvcreate.8_pregen
@@ -143,6 +143,10 @@ lvcreate - Create a logical volume
.ad b
.br
.ad l
+ \fB--nolocking\fP
+.ad b
+.br
+.ad l
\fB--nosync\fP
.ad b
.br
@@ -916,6 +920,10 @@ Common options for lvm:
.ad b
.br
.ad l
+[ \fB--nolocking\fP ]
+.ad b
+.br
+.ad l
[ \fB--profile\fP \fIString\fP ]
.ad b
.br
@@ -1254,6 +1262,12 @@ generated, where # is a number generated by LVM.
.ad b
.HP
.ad l
+\fB--nolocking\fP
+.br
+Disable locking.
+.ad b
+.HP
+.ad l
\fB--nosync\fP
.br
Causes the creation of mirror, raid1, raid4, raid5 and raid10 to skip the
diff --git a/man/lvdisplay.8_pregen b/man/lvdisplay.8_pregen
index 33e4f5d..6211a47 100644
--- a/man/lvdisplay.8_pregen
+++ b/man/lvdisplay.8_pregen
@@ -170,6 +170,10 @@ Common options for lvm:
.ad b
.br
.ad l
+[ \fB--nolocking\fP ]
+.ad b
+.br
+.ad l
[ \fB--profile\fP \fIString\fP ]
.ad b
.br
@@ -321,6 +325,12 @@ Useful if grepping the output.
.ad b
.HP
.ad l
+\fB--nolocking\fP
+.br
+Disable locking.
+.ad b
+.HP
+.ad l
\fB--nosuffix\fP
.br
Suppress the suffix on output sizes. Use with --units
diff --git a/man/lvextend.8_pregen b/man/lvextend.8_pregen
index 130672e..b29f508 100644
--- a/man/lvextend.8_pregen
+++ b/man/lvextend.8_pregen
@@ -63,6 +63,10 @@ lvextend - Add space to a logical volume
.ad b
.br
.ad l
+ \fB--nolocking\fP
+.ad b
+.br
+.ad l
\fB--nosync\fP
.ad b
.br
@@ -324,6 +328,10 @@ Common options for lvm:
.ad b
.br
.ad l
+[ \fB--nolocking\fP ]
+.ad b
+.br
+.ad l
[ \fB--profile\fP \fIString\fP ]
.ad b
.br
@@ -455,6 +463,12 @@ this option.
.ad b
.HP
.ad l
+\fB--nolocking\fP
+.br
+Disable locking.
+.ad b
+.HP
+.ad l
\fB--nosync\fP
.br
Causes the creation of mirror, raid1, raid4, raid5 and raid10 to skip the
diff --git a/man/lvm-fullreport.8_pregen b/man/lvm-fullreport.8_pregen
index 3fb00b1..4a8d7b3 100644
--- a/man/lvm-fullreport.8_pregen
+++ b/man/lvm-fullreport.8_pregen
@@ -72,10 +72,6 @@ if information changes between commands.
.ad b
.br
.ad l
-[ \fB--nolocking\fP ]
-.ad b
-.br
-.ad l
[ \fB--nosuffix\fP ]
.ad b
.br
@@ -170,6 +166,10 @@ Common options for lvm:
.ad b
.br
.ad l
+[ \fB--nolocking\fP ]
+.ad b
+.br
+.ad l
[ \fB--profile\fP \fIString\fP ]
.ad b
.br
diff --git a/man/lvm-lvpoll.8_pregen b/man/lvm-lvpoll.8_pregen
index 4591a28..1f4c129 100644
--- a/man/lvm-lvpoll.8_pregen
+++ b/man/lvm-lvpoll.8_pregen
@@ -84,6 +84,10 @@ Common options for lvm:
.ad b
.br
.ad l
+[ \fB--nolocking\fP ]
+.ad b
+.br
+.ad l
[ \fB--profile\fP \fIString\fP ]
.ad b
.br
@@ -169,6 +173,12 @@ Display long help text.
.ad b
.HP
.ad l
+\fB--nolocking\fP
+.br
+Disable locking.
+.ad b
+.HP
+.ad l
\fB--polloperation\fP \fBpvmove\fP|\fBconvert\fP|\fBmerge\fP|\fBmerge_thin\fP
.br
The command to perform from lvmpolld.
diff --git a/man/lvmconfig.8_pregen b/man/lvmconfig.8_pregen
index b47d589..0db2dd7 100644
--- a/man/lvmconfig.8_pregen
+++ b/man/lvmconfig.8_pregen
@@ -153,6 +153,10 @@ Common options for lvm:
.ad b
.br
.ad l
+[ \fB--nolocking\fP ]
+.ad b
+.br
+.ad l
[ \fB--profile\fP \fIString\fP ]
.ad b
.br
@@ -281,6 +285,12 @@ See \fBlvm.conf\fP(5) for more information about profiles.
.ad b
.HP
.ad l
+\fB--nolocking\fP
+.br
+Disable locking.
+.ad b
+.HP
+.ad l
\fB--profile\fP \fIString\fP
.br
An alias for --commandprofile or --metadataprofile, depending
diff --git a/man/lvmdiskscan.8_pregen b/man/lvmdiskscan.8_pregen
index 8b26d95..8eb57bf 100644
--- a/man/lvmdiskscan.8_pregen
+++ b/man/lvmdiskscan.8_pregen
@@ -79,6 +79,10 @@ Common options for lvm:
.ad b
.br
.ad l
+[ \fB--nolocking\fP ]
+.ad b
+.br
+.ad l
[ \fB--profile\fP \fIString\fP ]
.ad b
.br
@@ -144,6 +148,12 @@ Only report PVs.
.ad b
.HP
.ad l
+\fB--nolocking\fP
+.br
+Disable locking.
+.ad b
+.HP
+.ad l
\fB--profile\fP \fIString\fP
.br
An alias for --commandprofile or --metadataprofile, depending
diff --git a/man/lvreduce.8_pregen b/man/lvreduce.8_pregen
index 3aa4b5a..7acc042 100644
--- a/man/lvreduce.8_pregen
+++ b/man/lvreduce.8_pregen
@@ -110,6 +110,10 @@ Common options for lvm:
.ad b
.br
.ad l
+[ \fB--nolocking\fP ]
+.ad b
+.br
+.ad l
[ \fB--profile\fP \fIString\fP ]
.ad b
.br
@@ -214,6 +218,12 @@ this option.
.ad b
.HP
.ad l
+\fB--nolocking\fP
+.br
+Disable locking.
+.ad b
+.HP
+.ad l
\fB--noudevsync\fP
.br
Disables udev synchronisation. The process will not wait for notification
diff --git a/man/lvremove.8_pregen b/man/lvremove.8_pregen
index 88d0501..c835672 100644
--- a/man/lvremove.8_pregen
+++ b/man/lvremove.8_pregen
@@ -114,6 +114,10 @@ Common options for lvm:
.ad b
.br
.ad l
+[ \fB--nolocking\fP ]
+.ad b
+.br
+.ad l
[ \fB--profile\fP \fIString\fP ]
.ad b
.br
@@ -195,6 +199,12 @@ metadata/record_lvs_history is enabled.
.ad b
.HP
.ad l
+\fB--nolocking\fP
+.br
+Disable locking.
+.ad b
+.HP
+.ad l
\fB--noudevsync\fP
.br
Disables udev synchronisation. The process will not wait for notification
diff --git a/man/lvrename.8_pregen b/man/lvrename.8_pregen
index 69711e0..8d2f9b3 100644
--- a/man/lvrename.8_pregen
+++ b/man/lvrename.8_pregen
@@ -89,6 +89,10 @@ Common options for lvm:
.ad b
.br
.ad l
+[ \fB--nolocking\fP ]
+.ad b
+.br
+.ad l
[ \fB--profile\fP \fIString\fP ]
.ad b
.br
@@ -155,6 +159,12 @@ Display long help text.
.ad b
.HP
.ad l
+\fB--nolocking\fP
+.br
+Disable locking.
+.ad b
+.HP
+.ad l
\fB--noudevsync\fP
.br
Disables udev synchronisation. The process will not wait for notification
diff --git a/man/lvresize.8_pregen b/man/lvresize.8_pregen
index 8df157a..13246f5 100644
--- a/man/lvresize.8_pregen
+++ b/man/lvresize.8_pregen
@@ -59,6 +59,10 @@ lvresize - Resize a logical volume
.ad b
.br
.ad l
+ \fB--nolocking\fP
+.ad b
+.br
+.ad l
\fB--nosync\fP
.ad b
.br
@@ -272,6 +276,10 @@ Common options for lvm:
.ad b
.br
.ad l
+[ \fB--nolocking\fP ]
+.ad b
+.br
+.ad l
[ \fB--profile\fP \fIString\fP ]
.ad b
.br
@@ -397,6 +405,12 @@ this option.
.ad b
.HP
.ad l
+\fB--nolocking\fP
+.br
+Disable locking.
+.ad b
+.HP
+.ad l
\fB--nosync\fP
.br
Causes the creation of mirror, raid1, raid4, raid5 and raid10 to skip the
diff --git a/man/lvs.8_pregen b/man/lvs.8_pregen
index c6303bb..b5128f9 100644
--- a/man/lvs.8_pregen
+++ b/man/lvs.8_pregen
@@ -76,10 +76,6 @@ lvs produces formatted output about LVs.
.ad b
.br
.ad l
-[ \fB--nolocking\fP ]
-.ad b
-.br
-.ad l
[ \fB--nosuffix\fP ]
.ad b
.br
@@ -174,6 +170,10 @@ Common options for lvm:
.ad b
.br
.ad l
+[ \fB--nolocking\fP ]
+.ad b
+.br
+.ad l
[ \fB--profile\fP \fIString\fP ]
.ad b
.br
diff --git a/man/lvscan.8_pregen b/man/lvscan.8_pregen
index e9d90ee..489286e 100644
--- a/man/lvscan.8_pregen
+++ b/man/lvscan.8_pregen
@@ -105,6 +105,10 @@ Common options for lvm:
.ad b
.br
.ad l
+[ \fB--nolocking\fP ]
+.ad b
+.br
+.ad l
[ \fB--profile\fP \fIString\fP ]
.ad b
.br
@@ -191,6 +195,12 @@ Display long help text.
.ad b
.HP
.ad l
+\fB--nolocking\fP
+.br
+Disable locking.
+.ad b
+.HP
+.ad l
\fB--profile\fP \fIString\fP
.br
An alias for --commandprofile or --metadataprofile, depending
diff --git a/man/pvchange.8_pregen b/man/pvchange.8_pregen
index 6a80e72..5da8eca 100644
--- a/man/pvchange.8_pregen
+++ b/man/pvchange.8_pregen
@@ -154,6 +154,10 @@ Common options for lvm:
.ad b
.br
.ad l
+[ \fB--nolocking\fP ]
+.ad b
+.br
+.ad l
[ \fB--profile\fP \fIString\fP ]
.ad b
.br
@@ -269,6 +273,12 @@ If no, lvm will store metadata on the PV.
.ad b
.HP
.ad l
+\fB--nolocking\fP
+.br
+Disable locking.
+.ad b
+.HP
+.ad l
\fB--profile\fP \fIString\fP
.br
An alias for --commandprofile or --metadataprofile, depending
diff --git a/man/pvck.8_pregen b/man/pvck.8_pregen
index 1140d69..499f62c 100644
--- a/man/pvck.8_pregen
+++ b/man/pvck.8_pregen
@@ -69,6 +69,10 @@ Common options for lvm:
.ad b
.br
.ad l
+[ \fB--nolocking\fP ]
+.ad b
+.br
+.ad l
[ \fB--profile\fP \fIString\fP ]
.ad b
.br
@@ -137,6 +141,12 @@ Display long help text.
.ad b
.HP
.ad l
+\fB--nolocking\fP
+.br
+Disable locking.
+.ad b
+.HP
+.ad l
\fB--profile\fP \fIString\fP
.br
An alias for --commandprofile or --metadataprofile, depending
diff --git a/man/pvcreate.8_pregen b/man/pvcreate.8_pregen
index c4b03da..2b4fb1c 100644
--- a/man/pvcreate.8_pregen
+++ b/man/pvcreate.8_pregen
@@ -145,6 +145,10 @@ Common options for lvm:
.ad b
.br
.ad l
+[ \fB--nolocking\fP ]
+.ad b
+.br
+.ad l
[ \fB--profile\fP \fIString\fP ]
.ad b
.br
@@ -274,6 +278,12 @@ Specifies the type of on-disk metadata to use.
.ad b
.HP
.ad l
+\fB--nolocking\fP
+.br
+Disable locking.
+.ad b
+.HP
+.ad l
\fB--norestorefile\fP
.br
In conjunction with --uuid, this allows a uuid to be specified
diff --git a/man/pvdisplay.8_pregen b/man/pvdisplay.8_pregen
index bbf09d8..5f39e7b 100644
--- a/man/pvdisplay.8_pregen
+++ b/man/pvdisplay.8_pregen
@@ -166,6 +166,10 @@ Common options for lvm:
.ad b
.br
.ad l
+[ \fB--nolocking\fP ]
+.ad b
+.br
+.ad l
[ \fB--profile\fP \fIString\fP ]
.ad b
.br
@@ -306,6 +310,12 @@ Useful if grepping the output.
.ad b
.HP
.ad l
+\fB--nolocking\fP
+.br
+Disable locking.
+.ad b
+.HP
+.ad l
\fB--nosuffix\fP
.br
Suppress the suffix on output sizes. Use with --units
diff --git a/man/pvmove.8_pregen b/man/pvmove.8_pregen
index cb121f2..e553722 100644
--- a/man/pvmove.8_pregen
+++ b/man/pvmove.8_pregen
@@ -137,6 +137,10 @@ Common options for lvm:
.ad b
.br
.ad l
+[ \fB--nolocking\fP ]
+.ad b
+.br
+.ad l
[ \fB--profile\fP \fIString\fP ]
.ad b
.br
@@ -260,6 +264,12 @@ Move only the extents belonging to the named LV.
.ad b
.HP
.ad l
+\fB--nolocking\fP
+.br
+Disable locking.
+.ad b
+.HP
+.ad l
\fB--noudevsync\fP
.br
Disables udev synchronisation. The process will not wait for notification
diff --git a/man/pvremove.8_pregen b/man/pvremove.8_pregen
index 16a82ad..3d83478 100644
--- a/man/pvremove.8_pregen
+++ b/man/pvremove.8_pregen
@@ -79,6 +79,10 @@ Common options for lvm:
.ad b
.br
.ad l
+[ \fB--nolocking\fP ]
+.ad b
+.br
+.ad l
[ \fB--profile\fP \fIString\fP ]
.ad b
.br
@@ -145,6 +149,12 @@ Display long help text.
.ad b
.HP
.ad l
+\fB--nolocking\fP
+.br
+Disable locking.
+.ad b
+.HP
+.ad l
\fB--profile\fP \fIString\fP
.br
An alias for --commandprofile or --metadataprofile, depending
diff --git a/man/pvresize.8_pregen b/man/pvresize.8_pregen
index 7b61f81..5a5c975 100644
--- a/man/pvresize.8_pregen
+++ b/man/pvresize.8_pregen
@@ -74,6 +74,10 @@ Common options for lvm:
.ad b
.br
.ad l
+[ \fB--nolocking\fP ]
+.ad b
+.br
+.ad l
[ \fB--profile\fP \fIString\fP ]
.ad b
.br
@@ -133,6 +137,12 @@ Display long help text.
.ad b
.HP
.ad l
+\fB--nolocking\fP
+.br
+Disable locking.
+.ad b
+.HP
+.ad l
\fB--profile\fP \fIString\fP
.br
An alias for --commandprofile or --metadataprofile, depending
diff --git a/man/pvs.8_pregen b/man/pvs.8_pregen
index a2a62a6..59f1e3d 100644
--- a/man/pvs.8_pregen
+++ b/man/pvs.8_pregen
@@ -72,10 +72,6 @@ pvs produces formatted output about PVs.
.ad b
.br
.ad l
-[ \fB--nolocking\fP ]
-.ad b
-.br
-.ad l
[ \fB--nosuffix\fP ]
.ad b
.br
@@ -170,6 +166,10 @@ Common options for lvm:
.ad b
.br
.ad l
+[ \fB--nolocking\fP ]
+.ad b
+.br
+.ad l
[ \fB--profile\fP \fIString\fP ]
.ad b
.br
diff --git a/man/pvscan.8_pregen b/man/pvscan.8_pregen
index 230492b..3ce59b5 100644
--- a/man/pvscan.8_pregen
+++ b/man/pvscan.8_pregen
@@ -218,6 +218,10 @@ Common options for lvm:
.ad b
.br
.ad l
+[ \fB--nolocking\fP ]
+.ad b
+.br
+.ad l
[ \fB--profile\fP \fIString\fP ]
.ad b
.br
@@ -323,6 +327,12 @@ The minor number of a device.
.ad b
.HP
.ad l
+\fB--nolocking\fP
+.br
+Disable locking.
+.ad b
+.HP
+.ad l
\fB-n\fP|\fB--novolumegroup\fP
.br
Only show PVs not belonging to any VG.
diff --git a/man/vgcfgbackup.8_pregen b/man/vgcfgbackup.8_pregen
index e9c42fe..cd2521a 100644
--- a/man/vgcfgbackup.8_pregen
+++ b/man/vgcfgbackup.8_pregen
@@ -105,6 +105,10 @@ Common options for lvm:
.ad b
.br
.ad l
+[ \fB--nolocking\fP ]
+.ad b
+.br
+.ad l
[ \fB--profile\fP \fIString\fP ]
.ad b
.br
@@ -186,6 +190,12 @@ Display long help text.
.ad b
.HP
.ad l
+\fB--nolocking\fP
+.br
+Disable locking.
+.ad b
+.HP
+.ad l
\fB--profile\fP \fIString\fP
.br
An alias for --commandprofile or --metadataprofile, depending
diff --git a/man/vgcfgrestore.8_pregen b/man/vgcfgrestore.8_pregen
index cffd44b..b4e2183 100644
--- a/man/vgcfgrestore.8_pregen
+++ b/man/vgcfgrestore.8_pregen
@@ -55,6 +55,10 @@ vgcfgrestore - Restore volume group configuration
.ad b
.br
.ad l
+ \fB--nolocking\fP
+.ad b
+.br
+.ad l
\fB--profile\fP \fIString\fP
.ad b
.br
@@ -197,6 +201,10 @@ Common options for lvm:
.ad b
.br
.ad l
+[ \fB--nolocking\fP ]
+.ad b
+.br
+.ad l
[ \fB--profile\fP \fIString\fP ]
.ad b
.br
@@ -288,6 +296,12 @@ Specifies the type of on-disk metadata to use.
.ad b
.HP
.ad l
+\fB--nolocking\fP
+.br
+Disable locking.
+.ad b
+.HP
+.ad l
\fB--profile\fP \fIString\fP
.br
An alias for --commandprofile or --metadataprofile, depending
diff --git a/man/vgchange.8_pregen b/man/vgchange.8_pregen
index 00cfc2b..a9ac546 100644
--- a/man/vgchange.8_pregen
+++ b/man/vgchange.8_pregen
@@ -31,10 +31,6 @@ vgchange - Change volume group attributes
.ad b
.br
.ad l
- \fB-c\fP|\fB--clustered\fP \fBy\fP|\fBn\fP
-.ad b
-.br
-.ad l
\fB--commandprofile\fP \fIString\fP
.ad b
.br
@@ -119,6 +115,10 @@ vgchange - Change volume group attributes
.ad b
.br
.ad l
+ \fB--nolocking\fP
+.ad b
+.br
+.ad l
\fB--noudevsync\fP
.ad b
.br
@@ -218,10 +218,6 @@ required, after which the others are optional.
.ad b
.br
.ad l
- \fB-c\fP|\fB--clustered\fP \fBy\fP|\fBn\fP,
-.ad b
-.br
-.ad l
\fB-s\fP|\fB--physicalextentsize\fP \fISize\fP[m|UNIT],
.ad b
.br
@@ -637,6 +633,10 @@ Common options for lvm:
.ad b
.br
.ad l
+[ \fB--nolocking\fP ]
+.ad b
+.br
+.ad l
[ \fB--profile\fP \fIString\fP ]
.ad b
.br
@@ -722,13 +722,6 @@ Enabling this is strongly advised! See \fBvgcfgbackup\fP(8) for more information
.ad b
.HP
.ad l
-\fB-c\fP|\fB--clustered\fP \fBy\fP|\fBn\fP
-.br
-Change the clustered property of a VG using clvmd.
-See \fBclvmd\fP(8) for more information about clustered VGs.
-.ad b
-.HP
-.ad l
\fB--commandprofile\fP \fIString\fP
.br
The command profile to use for command configuration.
@@ -881,6 +874,12 @@ See \fBdmeventd\fP(8) for more information.
.ad b
.HP
.ad l
+\fB--nolocking\fP
+.br
+Disable locking.
+.ad b
+.HP
+.ad l
\fB--noudevsync\fP
.br
Disables udev synchronisation. The process will not wait for notification
diff --git a/man/vgck.8_pregen b/man/vgck.8_pregen
index 83e4a19..b1418f3 100644
--- a/man/vgck.8_pregen
+++ b/man/vgck.8_pregen
@@ -74,6 +74,10 @@ Common options for lvm:
.ad b
.br
.ad l
+[ \fB--nolocking\fP ]
+.ad b
+.br
+.ad l
[ \fB--profile\fP \fIString\fP ]
.ad b
.br
@@ -133,6 +137,12 @@ Display long help text.
.ad b
.HP
.ad l
+\fB--nolocking\fP
+.br
+Disable locking.
+.ad b
+.HP
+.ad l
\fB--profile\fP \fIString\fP
.br
An alias for --commandprofile or --metadataprofile, depending
diff --git a/man/vgconvert.8_pregen b/man/vgconvert.8_pregen
index 5e7f8a9..16e1fc8 100644
--- a/man/vgconvert.8_pregen
+++ b/man/vgconvert.8_pregen
@@ -95,6 +95,10 @@ Common options for lvm:
.ad b
.br
.ad l
+[ \fB--nolocking\fP ]
+.ad b
+.br
+.ad l
[ \fB--profile\fP \fIString\fP ]
.ad b
.br
@@ -199,6 +203,12 @@ Specifies the type of on-disk metadata to use.
.ad b
.HP
.ad l
+\fB--nolocking\fP
+.br
+Disable locking.
+.ad b
+.HP
+.ad l
\fB--profile\fP \fIString\fP
.br
An alias for --commandprofile or --metadataprofile, depending
diff --git a/man/vgcreate.8_pregen b/man/vgcreate.8_pregen
index 09bf126..cc990cc 100644
--- a/man/vgcreate.8_pregen
+++ b/man/vgcreate.8_pregen
@@ -152,6 +152,10 @@ Common options for lvm:
.ad b
.br
.ad l
+[ \fB--nolocking\fP ]
+.ad b
+.br
+.ad l
[ \fB--profile\fP \fIString\fP ]
.ad b
.br
@@ -199,11 +203,8 @@ Enabling this is strongly advised! See \fBvgcfgbackup\fP(8) for more information
.ad l
\fB-c\fP|\fB--clustered\fP \fBy\fP|\fBn\fP
.br
-Create a clustered VG using clvmd if LVM is compiled with cluster support.
-This allows multiple hosts to share a VG on shared devices.
-clvmd and a lock manager must be configured and running.
-(A clustered VG using clvmd is different from a shared VG using lvmlockd.)
-See \fBclvmd\fP(8) for more information about clustered VGs.
+This option was specific to clvm and is now replaced by
+the --shared option with \fBlvmlockd\fP(8).
.ad b
.HP
.ad l
@@ -332,6 +333,12 @@ Specifies the type of on-disk metadata to use.
.ad b
.HP
.ad l
+\fB--nolocking\fP
+.br
+Disable locking.
+.ad b
+.HP
+.ad l
\fB-s\fP|\fB--physicalextentsize\fP \fISize\fP[m|UNIT]
.br
Sets the physical extent size of PVs in the VG.
diff --git a/man/vgdisplay.8_pregen b/man/vgdisplay.8_pregen
index dcf9934..89e3603 100644
--- a/man/vgdisplay.8_pregen
+++ b/man/vgdisplay.8_pregen
@@ -161,6 +161,10 @@ Common options for lvm:
.ad b
.br
.ad l
+[ \fB--nolocking\fP ]
+.ad b
+.br
+.ad l
[ \fB--profile\fP \fIString\fP ]
.ad b
.br
@@ -295,6 +299,12 @@ Useful if grepping the output.
.ad b
.HP
.ad l
+\fB--nolocking\fP
+.br
+Disable locking.
+.ad b
+.HP
+.ad l
\fB--nosuffix\fP
.br
Suppress the suffix on output sizes. Use with --units
diff --git a/man/vgexport.8_pregen b/man/vgexport.8_pregen
index 1dd715e..a87c871 100644
--- a/man/vgexport.8_pregen
+++ b/man/vgexport.8_pregen
@@ -108,6 +108,10 @@ Common options for lvm:
.ad b
.br
.ad l
+[ \fB--nolocking\fP ]
+.ad b
+.br
+.ad l
[ \fB--profile\fP \fIString\fP ]
.ad b
.br
@@ -172,6 +176,12 @@ Display long help text.
.ad b
.HP
.ad l
+\fB--nolocking\fP
+.br
+Disable locking.
+.ad b
+.HP
+.ad l
\fB--profile\fP \fIString\fP
.br
An alias for --commandprofile or --metadataprofile, depending
diff --git a/man/vgextend.8_pregen b/man/vgextend.8_pregen
index a6a30e9..a2aff35 100644
--- a/man/vgextend.8_pregen
+++ b/man/vgextend.8_pregen
@@ -123,6 +123,10 @@ Common options for lvm:
.ad b
.br
.ad l
+[ \fB--nolocking\fP ]
+.ad b
+.br
+.ad l
[ \fB--profile\fP \fIString\fP ]
.ad b
.br
@@ -245,6 +249,12 @@ Specifies the type of on-disk metadata to use.
.ad b
.HP
.ad l
+\fB--nolocking\fP
+.br
+Disable locking.
+.ad b
+.HP
+.ad l
\fB--profile\fP \fIString\fP
.br
An alias for --commandprofile or --metadataprofile, depending
diff --git a/man/vgimport.8_pregen b/man/vgimport.8_pregen
index 71f9d56..f3d86df 100644
--- a/man/vgimport.8_pregen
+++ b/man/vgimport.8_pregen
@@ -98,6 +98,10 @@ Common options for lvm:
.ad b
.br
.ad l
+[ \fB--nolocking\fP ]
+.ad b
+.br
+.ad l
[ \fB--profile\fP \fIString\fP ]
.ad b
.br
@@ -170,6 +174,12 @@ Display long help text.
.ad b
.HP
.ad l
+\fB--nolocking\fP
+.br
+Disable locking.
+.ad b
+.HP
+.ad l
\fB--profile\fP \fIString\fP
.br
An alias for --commandprofile or --metadataprofile, depending
diff --git a/man/vgimportclone.8_pregen b/man/vgimportclone.8_pregen
index aab34f4..0d0eb84 100644
--- a/man/vgimportclone.8_pregen
+++ b/man/vgimportclone.8_pregen
@@ -78,6 +78,10 @@ Common options for lvm:
.ad b
.br
.ad l
+[ \fB--nolocking\fP ]
+.ad b
+.br
+.ad l
[ \fB--profile\fP \fIString\fP ]
.ad b
.br
@@ -154,6 +158,12 @@ Display long help text.
.ad b
.HP
.ad l
+\fB--nolocking\fP
+.br
+Disable locking.
+.ad b
+.HP
+.ad l
\fB--profile\fP \fIString\fP
.br
An alias for --commandprofile or --metadataprofile, depending
diff --git a/man/vgmerge.8_pregen b/man/vgmerge.8_pregen
index ab6873d..8b599f8 100644
--- a/man/vgmerge.8_pregen
+++ b/man/vgmerge.8_pregen
@@ -75,6 +75,10 @@ Common options for lvm:
.ad b
.br
.ad l
+[ \fB--nolocking\fP ]
+.ad b
+.br
+.ad l
[ \fB--profile\fP \fIString\fP ]
.ad b
.br
@@ -147,6 +151,12 @@ Display long help text.
.ad b
.HP
.ad l
+\fB--nolocking\fP
+.br
+Disable locking.
+.ad b
+.HP
+.ad l
\fB--profile\fP \fIString\fP
.br
An alias for --commandprofile or --metadataprofile, depending
diff --git a/man/vgmknodes.8_pregen b/man/vgmknodes.8_pregen
index 9588694..1cff786 100644
--- a/man/vgmknodes.8_pregen
+++ b/man/vgmknodes.8_pregen
@@ -86,6 +86,10 @@ Common options for lvm:
.ad b
.br
.ad l
+[ \fB--nolocking\fP ]
+.ad b
+.br
+.ad l
[ \fB--profile\fP \fIString\fP ]
.ad b
.br
@@ -152,6 +156,12 @@ Display long help text.
.ad b
.HP
.ad l
+\fB--nolocking\fP
+.br
+Disable locking.
+.ad b
+.HP
+.ad l
\fB--profile\fP \fIString\fP
.br
An alias for --commandprofile or --metadataprofile, depending
diff --git a/man/vgreduce.8_pregen b/man/vgreduce.8_pregen
index ecc5aca..fd92266 100644
--- a/man/vgreduce.8_pregen
+++ b/man/vgreduce.8_pregen
@@ -53,6 +53,10 @@ vgreduce - Remove physical volume(s) from a volume group
.ad b
.br
.ad l
+ \fB--nolocking\fP
+.ad b
+.br
+.ad l
\fB--profile\fP \fIString\fP
.ad b
.br
@@ -187,6 +191,10 @@ Common options for lvm:
.ad b
.br
.ad l
+[ \fB--nolocking\fP ]
+.ad b
+.br
+.ad l
[ \fB--profile\fP \fIString\fP ]
.ad b
.br
@@ -272,6 +280,12 @@ Only remove missing PVs from mirror LVs.
.ad b
.HP
.ad l
+\fB--nolocking\fP
+.br
+Disable locking.
+.ad b
+.HP
+.ad l
\fB--profile\fP \fIString\fP
.br
An alias for --commandprofile or --metadataprofile, depending
diff --git a/man/vgremove.8_pregen b/man/vgremove.8_pregen
index f562991..69f2831 100644
--- a/man/vgremove.8_pregen
+++ b/man/vgremove.8_pregen
@@ -89,6 +89,10 @@ Common options for lvm:
.ad b
.br
.ad l
+[ \fB--nolocking\fP ]
+.ad b
+.br
+.ad l
[ \fB--profile\fP \fIString\fP ]
.ad b
.br
@@ -155,6 +159,12 @@ Display long help text.
.ad b
.HP
.ad l
+\fB--nolocking\fP
+.br
+Disable locking.
+.ad b
+.HP
+.ad l
\fB--noudevsync\fP
.br
Disables udev synchronisation. The process will not wait for notification
diff --git a/man/vgrename.8_pregen b/man/vgrename.8_pregen
index 1f8928e..0313cf8 100644
--- a/man/vgrename.8_pregen
+++ b/man/vgrename.8_pregen
@@ -102,6 +102,10 @@ Common options for lvm:
.ad b
.br
.ad l
+[ \fB--nolocking\fP ]
+.ad b
+.br
+.ad l
[ \fB--profile\fP \fIString\fP ]
.ad b
.br
@@ -175,6 +179,12 @@ Display long help text.
.ad b
.HP
.ad l
+\fB--nolocking\fP
+.br
+Disable locking.
+.ad b
+.HP
+.ad l
\fB--profile\fP \fIString\fP
.br
An alias for --commandprofile or --metadataprofile, depending
diff --git a/man/vgs.8_pregen b/man/vgs.8_pregen
index efceb4e..681711b 100644
--- a/man/vgs.8_pregen
+++ b/man/vgs.8_pregen
@@ -68,10 +68,6 @@ vgs produces formatted output about VGs.
.ad b
.br
.ad l
-[ \fB--nolocking\fP ]
-.ad b
-.br
-.ad l
[ \fB--nosuffix\fP ]
.ad b
.br
@@ -166,6 +162,10 @@ Common options for lvm:
.ad b
.br
.ad l
+[ \fB--nolocking\fP ]
+.ad b
+.br
+.ad l
[ \fB--profile\fP \fIString\fP ]
.ad b
.br
diff --git a/man/vgscan.8_pregen b/man/vgscan.8_pregen
index 18b2990..fe9f201 100644
--- a/man/vgscan.8_pregen
+++ b/man/vgscan.8_pregen
@@ -85,6 +85,10 @@ Common options for lvm:
.ad b
.br
.ad l
+[ \fB--nolocking\fP ]
+.ad b
+.br
+.ad l
[ \fB--profile\fP \fIString\fP ]
.ad b
.br
@@ -164,6 +168,12 @@ LVs and creates any missing ones and removes unused ones.
.ad b
.HP
.ad l
+\fB--nolocking\fP
+.br
+Disable locking.
+.ad b
+.HP
+.ad l
\fB--notifydbus\fP
.br
Send a notification to D-Bus. The command will exit with an error
diff --git a/man/vgsplit.8_pregen b/man/vgsplit.8_pregen
index 6c3e6ec..c1e7f4e 100644
--- a/man/vgsplit.8_pregen
+++ b/man/vgsplit.8_pregen
@@ -50,10 +50,6 @@ Common options for command:
.ad b
.br
.ad l
-[ \fB-c\fP|\fB--clustered\fP \fBy\fP|\fBn\fP ]
-.ad b
-.br
-.ad l
[ \fB-l\fP|\fB--maxlogicalvolumes\fP \fINumber\fP ]
.ad b
.br
@@ -122,6 +118,10 @@ Common options for lvm:
.ad b
.br
.ad l
+[ \fB--nolocking\fP ]
+.ad b
+.br
+.ad l
[ \fB--profile\fP \fIString\fP ]
.ad b
.br
@@ -160,12 +160,6 @@ Enabling this is strongly advised! See \fBvgcfgbackup\fP(8) for more information
.ad b
.HP
.ad l
-\fB-c\fP|\fB--clustered\fP \fBy\fP|\fBn\fP
-.br
-Specifies the clustered property of the new VG.
-.ad b
-.HP
-.ad l
\fB--commandprofile\fP \fIString\fP
.br
The command profile to use for command configuration.
@@ -244,6 +238,12 @@ Move only PVs used by the named LV.
.ad b
.HP
.ad l
+\fB--nolocking\fP
+.br
+Disable locking.
+.ad b
+.HP
+.ad l
\fB--profile\fP \fIString\fP
.br
An alias for --commandprofile or --metadataprofile, depending
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=962a3eb3faced455b0a10…
Commit: 962a3eb3faced455b0a10f8e7d4575bed65b8dbd
Parent: d5da55ed85248adb066d293c2a1b863ce17d2779
Author: Joe Thornber <ejt(a)redhat.com>
AuthorDate: Fri Jun 8 13:44:43 2018 +0100
Committer: Joe Thornber <ejt(a)redhat.com>
CommitterDate: Fri Jun 8 13:44:43 2018 +0100
device_mapper: remove c++ guards from the header
This isn't a public header anymore, so not needed.
---
device_mapper/all.h | 7 -------
1 files changed, 0 insertions(+), 7 deletions(-)
diff --git a/device_mapper/all.h b/device_mapper/all.h
index b1d5a8c..ca0dbc7 100644
--- a/device_mapper/all.h
+++ b/device_mapper/all.h
@@ -43,10 +43,6 @@
#define DM_ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
-#ifdef __cplusplus
-extern "C" {
-#endif
-
/*****************************************************************
* The first section of this file provides direct access to the
* individual device-mapper ioctls. Since it is quite laborious to
@@ -3518,7 +3514,4 @@ int dm_udev_wait_immediate(uint32_t cookie, int *ready);
#define DM_DEV_DIR_UMASK 0022
#define DM_CONTROL_NODE_UMASK 0177
-#ifdef __cplusplus
-}
-#endif
#endif /* LIB_DEVICE_MAPPER_H */
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=286c1ba3369d190148042…
Commit: 286c1ba3369d1901480425e305e313e8ea365da4
Parent: 88ae928ca3f950fa0ecb8b8e0aff118b7360a9d7
Author: Joe Thornber <ejt(a)redhat.com>
AuthorDate: Fri Jun 8 12:31:45 2018 +0100
Committer: Joe Thornber <ejt(a)redhat.com>
CommitterDate: Fri Jun 8 12:31:45 2018 +0100
device_mapper: rename libdevmapper.h -> all.h
I'm paranoid a file will include the global one in /usr/include
by accident.
---
daemons/cmirrord/cluster.h | 2 +-
device_mapper/all.h | 3554 ++++++++++++++++++++
device_mapper/libdevmapper.h | 3554 --------------------
device_mapper/libdm-common.h | 2 +-
device_mapper/misc/dm-logging.h | 2 +-
device_mapper/misc/dmlib.h | 2 +-
device_mapper/vdo/status.c | 2 +-
lib/config/config.h | 2 +-
lib/device/bcache.h | 2 +-
lib/metadata/pv.h | 2 +-
lib/metadata/vg.h | 2 +-
lib/misc/lib.h | 2 +-
lib/report/properties.h | 2 +-
.../lvm2_activation_generator_systemd_red_hat.c | 2 +-
test/unit/activation-generator_t.c | 2 +-
test/unit/bitset_t.c | 2 +-
test/unit/config_t.c | 2 +-
test/unit/dmlist_t.c | 2 +-
test/unit/dmstatus_t.c | 2 +-
test/unit/framework.h | 2 +-
test/unit/matcher_t.c | 2 +-
test/unit/percent_t.c | 2 +-
test/unit/string_t.c | 2 +-
tools/tool.h | 2 +-
24 files changed, 3576 insertions(+), 3576 deletions(-)
diff --git a/daemons/cmirrord/cluster.h b/daemons/cmirrord/cluster.h
index 54ddd79..01d4871 100644
--- a/daemons/cmirrord/cluster.h
+++ b/daemons/cmirrord/cluster.h
@@ -13,7 +13,7 @@
#define _LVM_CLOG_CLUSTER_H
#include "device_mapper/misc/dm-log-userspace.h"
-#include "device_mapper/libdevmapper.h"
+#include "device_mapper/all.h"
#define DM_ULOG_RESPONSE 0x1000U /* in last byte of 32-bit value */
#define DM_ULOG_CHECKPOINT_READY 21
diff --git a/device_mapper/all.h b/device_mapper/all.h
new file mode 100644
index 0000000..f7ff4ce
--- /dev/null
+++ b/device_mapper/all.h
@@ -0,0 +1,3554 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2017 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2006 Rackable Systems All rights reserved.
+ *
+ * This file is part of the device-mapper userspace tools.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef LIB_DEVICE_MAPPER_H
+#define LIB_DEVICE_MAPPER_H
+
+#include <inttypes.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifdef __linux__
+# include <linux/types.h>
+#endif
+
+#include <limits.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "base/data-struct/list.h"
+
+#include "base/data-struct/list.h"
+
+#ifndef __GNUC__
+# define __typeof__ typeof
+#endif
+
+/* Macros to make string defines */
+#define DM_TO_STRING_EXP(A) #A
+#define DM_TO_STRING(A) DM_TO_STRING_EXP(A)
+
+#define DM_ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*****************************************************************
+ * The first section of this file provides direct access to the
+ * individual device-mapper ioctls. Since it is quite laborious to
+ * build the ioctl arguments for the device-mapper, people are
+ * encouraged to use this library.
+ ****************************************************************/
+
+/*
+ * The library user may wish to register their own
+ * logging function. By default errors go to stderr.
+ * Use dm_log_with_errno_init(NULL) to restore the default log fn.
+ * Error messages may have a non-zero errno.
+ * Debug messages may have a non-zero class.
+ * Aborts on internal error when env DM_ABORT_ON_INTERNAL_ERRORS is 1
+ */
+
+typedef void (*dm_log_with_errno_fn) (int level, const char *file, int line,
+ int dm_errno_or_class, const char *f, ...)
+ __attribute__ ((format(printf, 5, 6)));
+
+void dm_log_with_errno_init(dm_log_with_errno_fn fn);
+void dm_log_init_verbose(int level);
+
+/*
+ * Original version of this function.
+ * dm_errno is set to 0.
+ *
+ * Deprecated: Use the _with_errno_ versions above instead.
+ */
+typedef void (*dm_log_fn) (int level, const char *file, int line,
+ const char *f, ...)
+ __attribute__ ((format(printf, 4, 5)));
+
+void dm_log_init(dm_log_fn fn);
+/*
+ * For backward-compatibility, indicate that dm_log_init() was used
+ * to set a non-default value of dm_log().
+ */
+int dm_log_is_non_default(void);
+
+/*
+ * Number of devices currently in suspended state (via the library).
+ */
+int dm_get_suspended_counter(void);
+
+enum {
+ DM_DEVICE_CREATE,
+ DM_DEVICE_RELOAD,
+ DM_DEVICE_REMOVE,
+ DM_DEVICE_REMOVE_ALL,
+
+ DM_DEVICE_SUSPEND,
+ DM_DEVICE_RESUME,
+
+ DM_DEVICE_INFO,
+ DM_DEVICE_DEPS,
+ DM_DEVICE_RENAME,
+
+ DM_DEVICE_VERSION,
+
+ DM_DEVICE_STATUS,
+ DM_DEVICE_TABLE,
+ DM_DEVICE_WAITEVENT,
+
+ DM_DEVICE_LIST,
+
+ DM_DEVICE_CLEAR,
+
+ DM_DEVICE_MKNODES,
+
+ DM_DEVICE_LIST_VERSIONS,
+
+ DM_DEVICE_TARGET_MSG,
+
+ DM_DEVICE_SET_GEOMETRY
+};
+
+/*
+ * You will need to build a struct dm_task for
+ * each ioctl command you want to execute.
+ */
+
+struct dm_pool;
+struct dm_task;
+struct dm_timestamp;
+
+struct dm_task *dm_task_create(int type);
+void dm_task_destroy(struct dm_task *dmt);
+
+int dm_task_set_name(struct dm_task *dmt, const char *name);
+int dm_task_set_uuid(struct dm_task *dmt, const char *uuid);
+
+/*
+ * Retrieve attributes after an info.
+ */
+struct dm_info {
+ int exists;
+ int suspended;
+ int live_table;
+ int inactive_table;
+ int32_t open_count;
+ uint32_t event_nr;
+ uint32_t major;
+ uint32_t minor; /* minor device number */
+ int read_only; /* 0:read-write; 1:read-only */
+
+ int32_t target_count;
+
+ int deferred_remove;
+ int internal_suspend;
+};
+
+struct dm_deps {
+ uint32_t count;
+ uint32_t filler;
+ uint64_t device[0];
+};
+
+struct dm_names {
+ uint64_t dev;
+ uint32_t next; /* Offset to next struct from start of this struct */
+ char name[0];
+};
+
+struct dm_versions {
+ uint32_t next; /* Offset to next struct from start of this struct */
+ uint32_t version[3];
+
+ char name[0];
+};
+
+int dm_get_library_version(char *version, size_t size);
+int dm_task_get_driver_version(struct dm_task *dmt, char *version, size_t size);
+int dm_task_get_info(struct dm_task *dmt, struct dm_info *dmi);
+
+/*
+ * This function returns dm device's UUID based on the value
+ * of the mangling mode set during preceding dm_task_run call:
+ * - unmangled UUID for DM_STRING_MANGLING_{AUTO, HEX},
+ * - UUID without any changes for DM_STRING_MANGLING_NONE.
+ *
+ * To get mangled or unmangled form of the UUID directly, use
+ * dm_task_get_uuid_mangled or dm_task_get_uuid_unmangled function.
+ */
+const char *dm_task_get_uuid(const struct dm_task *dmt);
+
+struct dm_deps *dm_task_get_deps(struct dm_task *dmt);
+struct dm_versions *dm_task_get_versions(struct dm_task *dmt);
+const char *dm_task_get_message_response(struct dm_task *dmt);
+
+/*
+ * These functions return device-mapper names based on the value
+ * of the mangling mode set during preceding dm_task_run call:
+ * - unmangled name for DM_STRING_MANGLING_{AUTO, HEX},
+ * - name without any changes for DM_STRING_MANGLING_NONE.
+ *
+ * To get mangled or unmangled form of the name directly, use
+ * dm_task_get_name_mangled or dm_task_get_name_unmangled function.
+ */
+const char *dm_task_get_name(const struct dm_task *dmt);
+struct dm_names *dm_task_get_names(struct dm_task *dmt);
+
+int dm_task_set_ro(struct dm_task *dmt);
+int dm_task_set_newname(struct dm_task *dmt, const char *newname);
+int dm_task_set_newuuid(struct dm_task *dmt, const char *newuuid);
+int dm_task_set_minor(struct dm_task *dmt, int minor);
+int dm_task_set_major(struct dm_task *dmt, int major);
+int dm_task_set_major_minor(struct dm_task *dmt, int major, int minor, int allow_default_major_fallback);
+int dm_task_set_uid(struct dm_task *dmt, uid_t uid);
+int dm_task_set_gid(struct dm_task *dmt, gid_t gid);
+int dm_task_set_mode(struct dm_task *dmt, mode_t mode);
+/* See also description for DM_UDEV_DISABLE_LIBRARY_FALLBACK flag! */
+int dm_task_set_cookie(struct dm_task *dmt, uint32_t *cookie, uint16_t flags);
+int dm_task_set_event_nr(struct dm_task *dmt, uint32_t event_nr);
+int dm_task_set_geometry(struct dm_task *dmt, const char *cylinders, const char *heads, const char *sectors, const char *start);
+int dm_task_set_message(struct dm_task *dmt, const char *message);
+int dm_task_set_sector(struct dm_task *dmt, uint64_t sector);
+int dm_task_no_flush(struct dm_task *dmt);
+int dm_task_no_open_count(struct dm_task *dmt);
+int dm_task_skip_lockfs(struct dm_task *dmt);
+int dm_task_query_inactive_table(struct dm_task *dmt);
+int dm_task_suppress_identical_reload(struct dm_task *dmt);
+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.
+ */
+int dm_task_enable_checks(struct dm_task *dmt);
+
+typedef enum {
+ DM_ADD_NODE_ON_RESUME, /* add /dev/mapper node with dmsetup resume */
+ DM_ADD_NODE_ON_CREATE /* add /dev/mapper node with dmsetup create */
+} dm_add_node_t;
+int dm_task_set_add_node(struct dm_task *dmt, dm_add_node_t add_node);
+
+/*
+ * Control read_ahead.
+ */
+#define DM_READ_AHEAD_AUTO UINT32_MAX /* Use kernel default readahead */
+#define DM_READ_AHEAD_NONE 0 /* Disable readahead */
+
+#define DM_READ_AHEAD_MINIMUM_FLAG 0x1 /* Value supplied is minimum */
+
+/*
+ * Read ahead is set with DM_DEVICE_CREATE with a table or DM_DEVICE_RESUME.
+ */
+int dm_task_set_read_ahead(struct dm_task *dmt, uint32_t read_ahead,
+ uint32_t read_ahead_flags);
+uint32_t dm_task_get_read_ahead(const struct dm_task *dmt,
+ uint32_t *read_ahead);
+
+/*
+ * Use these to prepare for a create or reload.
+ */
+int dm_task_add_target(struct dm_task *dmt,
+ uint64_t start,
+ uint64_t size, const char *ttype, const char *params);
+
+/*
+ * Format major/minor numbers correctly for input to driver.
+ */
+#define DM_FORMAT_DEV_BUFSIZE 13 /* Minimum bufsize to handle worst case. */
+int dm_format_dev(char *buf, int bufsize, uint32_t dev_major, uint32_t dev_minor);
+
+/* Use this to retrive target information returned from a STATUS call */
+void *dm_get_next_target(struct dm_task *dmt,
+ void *next, uint64_t *start, uint64_t *length,
+ char **target_type, char **params);
+
+/*
+ * Following dm_get_status_* functions will allocate approriate status structure
+ * from passed mempool together with the necessary character arrays.
+ * Destroying the mempool will release all asociated allocation.
+ */
+
+/* Parse params from STATUS call for mirror target */
+typedef enum {
+ DM_STATUS_MIRROR_ALIVE = 'A',/* No failures */
+ DM_STATUS_MIRROR_FLUSH_FAILED = 'F',/* Mirror out-of-sync */
+ DM_STATUS_MIRROR_WRITE_FAILED = 'D',/* Mirror out-of-sync */
+ DM_STATUS_MIRROR_SYNC_FAILED = 'S',/* Mirror out-of-sync */
+ DM_STATUS_MIRROR_READ_FAILED = 'R',/* Mirror data unaffected */
+ DM_STATUS_MIRROR_UNCLASSIFIED = 'U' /* Bug */
+} dm_status_mirror_health_t;
+
+struct dm_status_mirror {
+ uint64_t total_regions;
+ uint64_t insync_regions;
+ uint32_t dev_count; /* # of devs[] elements (<= 8) */
+ struct {
+ dm_status_mirror_health_t health;
+ uint32_t major;
+ uint32_t minor;
+ } *devs; /* array with individual legs */
+ const char *log_type; /* core, disk,.... */
+ uint32_t log_count; /* # of logs[] elements */
+ struct {
+ dm_status_mirror_health_t health;
+ uint32_t major;
+ uint32_t minor;
+ } *logs; /* array with individual logs */
+};
+
+int dm_get_status_mirror(struct dm_pool *mem, const char *params,
+ struct dm_status_mirror **status);
+
+/* Parse params from STATUS call for raid target */
+struct dm_status_raid {
+ uint64_t reserved;
+ uint64_t total_regions; /* sectors */
+ uint64_t insync_regions; /* sectors */
+ uint64_t mismatch_count;
+ uint32_t dev_count;
+ char *raid_type;
+ /* A - alive, a - alive not in-sync, D - dead/failed */
+ char *dev_health;
+ /* idle, frozen, resync, recover, check, repair */
+ char *sync_action;
+ uint64_t data_offset; /* RAID out-of-place reshaping */
+};
+
+int dm_get_status_raid(struct dm_pool *mem, const char *params,
+ struct dm_status_raid **status);
+
+/* Parse params from STATUS call for cache target */
+struct dm_status_cache {
+ uint64_t version; /* zero for now */
+
+ uint32_t metadata_block_size; /* in 512B sectors */
+ uint32_t block_size; /* AKA 'chunk_size' */
+
+ uint64_t metadata_used_blocks;
+ uint64_t metadata_total_blocks;
+
+ uint64_t used_blocks;
+ uint64_t dirty_blocks;
+ uint64_t total_blocks;
+
+ uint64_t read_hits;
+ uint64_t read_misses;
+ uint64_t write_hits;
+ uint64_t write_misses;
+
+ uint64_t demotions;
+ uint64_t promotions;
+
+ uint64_t feature_flags; /* DM_CACHE_FEATURE_? */
+
+ int core_argc;
+ char **core_argv;
+
+ char *policy_name;
+ int policy_argc;
+ char **policy_argv;
+
+ unsigned error : 1; /* detected error (switches to fail soon) */
+ unsigned fail : 1; /* all I/O fails */
+ unsigned needs_check : 1; /* metadata needs check */
+ unsigned read_only : 1; /* metadata may not be changed */
+ uint32_t reserved : 28;
+};
+
+int dm_get_status_cache(struct dm_pool *mem, const char *params,
+ struct dm_status_cache **status);
+
+/*
+ * Parse params from STATUS call for snapshot target
+ *
+ * Snapshot target's format:
+ * <= 1.7.0: <used_sectors>/<total_sectors>
+ * >= 1.8.0: <used_sectors>/<total_sectors> <metadata_sectors>
+ */
+struct dm_status_snapshot {
+ uint64_t used_sectors; /* in 512b units */
+ uint64_t total_sectors;
+ uint64_t metadata_sectors;
+ unsigned has_metadata_sectors : 1; /* set when metadata_sectors is present */
+ unsigned invalid : 1; /* set when snapshot is invalidated */
+ unsigned merge_failed : 1; /* set when snapshot merge failed */
+ unsigned overflow : 1; /* set when snapshot overflows */
+};
+
+int dm_get_status_snapshot(struct dm_pool *mem, const char *params,
+ struct dm_status_snapshot **status);
+
+/* Parse params from STATUS call for thin_pool target */
+typedef enum {
+ DM_THIN_DISCARDS_IGNORE,
+ DM_THIN_DISCARDS_NO_PASSDOWN,
+ DM_THIN_DISCARDS_PASSDOWN
+} dm_thin_discards_t;
+
+struct dm_status_thin_pool {
+ uint64_t transaction_id;
+ uint64_t used_metadata_blocks;
+ uint64_t total_metadata_blocks;
+ uint64_t used_data_blocks;
+ uint64_t total_data_blocks;
+ uint64_t held_metadata_root;
+ uint32_t read_only; /* metadata may not be changed */
+ dm_thin_discards_t discards;
+ uint32_t fail : 1; /* all I/O fails */
+ uint32_t error_if_no_space : 1; /* otherwise queue_if_no_space */
+ uint32_t out_of_data_space : 1; /* metadata may be changed, but data may not be allocated (no rw) */
+ uint32_t needs_check : 1; /* metadata needs check */
+ uint32_t error : 1; /* detected error (switches to fail soon) */
+ uint32_t reserved : 27;
+};
+
+int dm_get_status_thin_pool(struct dm_pool *mem, const char *params,
+ struct dm_status_thin_pool **status);
+
+/* Parse params from STATUS call for thin target */
+struct dm_status_thin {
+ uint64_t mapped_sectors;
+ uint64_t highest_mapped_sector;
+ uint32_t fail : 1; /* Thin volume fails I/O */
+ uint32_t reserved : 31;
+};
+
+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
+ * and obtaining and manipulating current counter values from the
+ * kernel. Methods are provided to return baisc count values and to
+ * derive time-based metrics when a suitable interval estimate is
+ * provided.
+ *
+ * Internally the dm_stats handle contains a pointer to a table of one
+ * or more dm_stats_region objects representing the regions registered
+ * with the dm_stats_create_region() method. These in turn point to a
+ * table of one or more dm_stats_counters objects containing the
+ * counter sets for each defined area within the region:
+ *
+ * dm_stats->dm_stats_region[nr_regions]->dm_stats_counters[nr_areas]
+ *
+ * This structure is private to the library and may change in future
+ * versions: all users should make use of the public interface and treat
+ * the dm_stats type as an opaque handle.
+ *
+ * Regions and counter sets are stored in order of increasing region_id.
+ * Depending on region specifications and the sequence of create and
+ * delete operations this may not correspond to increasing sector
+ * number: users of the library should not assume that this is the case
+ * unless region creation is deliberately managed to ensure this (by
+ * always creating regions in strict order of ascending sector address).
+ *
+ * Regions may also overlap so the same sector range may be included in
+ * more than one region or area: applications should be prepared to deal
+ * with this or manage regions such that it does not occur.
+ */
+struct dm_stats;
+
+/*
+ * Histogram handle.
+ *
+ * A histogram object represents the latency histogram values and bin
+ * boundaries of the histogram associated with a particular area.
+ *
+ * Operations on the handle allow the number of bins, bin boundaries,
+ * counts and relative proportions to be obtained as well as the
+ * conversion of a histogram or its bounds to a compact string
+ * representation.
+ */
+struct dm_histogram;
+
+/*
+ * 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.
+ *
+ * If program_id is NULL or the empty string a program_id will be
+ * automatically set to the value contained in /proc/self/comm.
+ */
+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);
+
+/*
+ * Bind a dm_stats handle to the device backing the file referenced
+ * by the specified file descriptor.
+ *
+ * File descriptor fd must reference a regular file, open for reading,
+ * in a local file system, backed by a device-mapper device, that
+ * supports the FIEMAP ioctl, and that returns data describing the
+ * physical location of extents.
+ */
+int dm_stats_bind_from_fd(struct dm_stats *dms, int fd);
+/*
+ * Test whether the running kernel supports the precise_timestamps
+ * feature. Presence of this feature also implies histogram support.
+ * The library will check this call internally and fails any attempt
+ * to use nanosecond counters or histograms on kernels that fail to
+ * meet this check.
+ */
+int dm_message_supports_precise_timestamps(void);
+
+/*
+ * Precise timetamps and histogram support.
+ *
+ * Test for the presence of precise_timestamps and histogram support.
+ */
+int dm_stats_driver_supports_precise(void);
+int dm_stats_driver_supports_histogram(void);
+
+/*
+ * Returns 1 if the specified region has the precise_timestamps feature
+ * enabled (i.e. produces nanosecond-precision counter values) or 0 for
+ * a region using the default milisecond precision.
+ */
+int dm_stats_get_region_precise_timestamps(const struct dm_stats *dms,
+ uint64_t region_id);
+
+/*
+ * Returns 1 if the region at the current cursor location has the
+ * precise_timestamps feature enabled (i.e. produces
+ * nanosecond-precision counter values) or 0 for a region using the
+ * default milisecond precision.
+ */
+int dm_stats_get_current_region_precise_timestamps(const struct dm_stats *dms);
+
+#define DM_STATS_ALL_PROGRAMS ""
+/*
+ * Parse the response from a @stats_list message. dm_stats_list will
+ * allocate the necessary dm_stats and dm_stats region structures from
+ * the embedded dm_pool. No counter data will be obtained (the counters
+ * members of dm_stats_region objects are set to NULL).
+ *
+ * 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
+ * the argument is NULL then the default program_id associated with the
+ * dm_stats handle will be used. Passing the special value
+ * DM_STATS_ALL_PROGRAMS will cause all regions to be queried
+ * regardless of region program_id.
+ */
+int dm_stats_list(struct dm_stats *dms, const char *program_id);
+
+#define DM_STATS_REGIONS_ALL UINT64_MAX
+/*
+ * 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
+ * the argument is NULL then the default program_id associated with the
+ * dm_stats handle will be used. Passing the special value
+ * DM_STATS_ALL_PROGRAMS will cause all regions to be queried
+ * regardless of region program_id.
+ *
+ * Passing the special value DM_STATS_REGIONS_ALL as the region_id
+ * argument will attempt to retrieve all regions selected by the
+ * program_id argument.
+ *
+ * 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.
+ * Passing zero for both start and len will create a region spanning
+ * the entire device.
+ *
+ * Step determines how to subdivide the region into discrete counter
+ * sets: a positive value specifies the size of areas into which the
+ * region should be split while a negative value will split the region
+ * into a number of areas equal to the absolute value of step:
+ *
+ * - a region with one area spanning the entire device:
+ *
+ * dm_stats_create_region(dms, 0, 0, -1, p, a);
+ *
+ * - a region with areas of 1MiB:
+ *
+ * dm_stats_create_region(dms, 0, 0, 1 << 11, p, a);
+ *
+ * - one 1MiB region starting at 1024 sectors with two areas:
+ *
+ * dm_stats_create_region(dms, 1024, 1 << 11, -2, p, a);
+ *
+ * If precise is non-zero attempt to create a region with nanosecond
+ * precision counters using the kernel precise_timestamps feature.
+ *
+ * precise - A flag to request nanosecond precision counters
+ * to be used for this region.
+ *
+ * histogram_bounds - specify the boundaries of a latency histogram to
+ * be tracked for the region. The values are expressed as an array of
+ * uint64_t terminated with a zero. Values must be in order of ascending
+ * magnitude and specify the upper bounds of successive histogram bins
+ * in nanoseconds (with an implicit lower bound of zero on the first bin
+ * and an implicit upper bound of infinity on the final bin). For
+ * example:
+ *
+ * uint64_t bounds_ary[] = { 1000, 2000, 3000, 0 };
+ *
+ * Specifies a histogram with four bins: 0-1000ns, 1000-2000ns,
+ * 2000-3000ns and >3000ns.
+ *
+ * The smallest latency value that can be tracked for a region not using
+ * precise_timestamps is 1ms: attempting to create a region with
+ * histogram boundaries < 1ms will cause the precise_timestamps feature
+ * to be enabled for that region automatically if it was not requested
+ * explicitly.
+ *
+ * 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.
+ *
+ * user_data is an optional string argument that is added to the
+ * content of the aux_data field stored with the statistics region by
+ * the kernel.
+ *
+ * The library may also use this space internally, for example, to
+ * store a group descriptor or other metadata: in this case the
+ * library will strip any internal data fields from the value before
+ * it is returned via a call to dm_stats_get_region_aux_data().
+ *
+ * The user data stored is not accessed by the library or kernel and
+ * may be used to store an arbitrary data word (embedded whitespace is
+ * not permitted).
+ *
+ * An application using both the library and direct access to the
+ * @stats_list device-mapper message may see the internal values stored
+ * in this field by the library. In such cases any string up to and
+ * including the first '#' in the field must be treated as an opaque
+ * value and preserved across any external modification of aux_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,
+ int precise, struct dm_histogram *bounds,
+ const char *program_id, const char *user_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.
+ */
+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).
+ *
+ * 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
+ * numeric form accessible via the dm_stats_get_*() counter access
+ * methods.
+ *
+ * A subset of the data lines may be requested by setting the
+ * 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).
+ */
+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
+ * 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).
+ *
+ * Always returns zero on an empty handle.
+ */
+uint64_t dm_stats_get_nr_regions(const struct dm_stats *dms);
+
+/*
+ * Determine the number of groups contained in a dm_stats handle
+ * following a dm_stats_list() or dm_stats_populate() call.
+ *
+ * The value returned is the number of registered groups visible with the
+ * progam_id value used for the list or populate operation and may not be
+ * equal to the highest present group_id (either due to program_id
+ * filtering or gaps in the sequence of group_id values).
+ *
+ * Always returns zero on an empty handle.
+ */
+uint64_t dm_stats_get_nr_groups(const struct dm_stats *dms);
+
+/*
+ * Test whether region_id is present in this dm_stats handle.
+ */
+int dm_stats_region_present(const 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(const struct dm_stats *dms,
+ uint64_t region_id);
+
+/*
+ * Returns the total number of areas (counter sets) in all regions of the
+ * given dm_stats object.
+ */
+uint64_t dm_stats_get_nr_areas(const struct dm_stats *dms);
+
+/*
+ * Test whether group_id is present in this dm_stats handle.
+ */
+int dm_stats_group_present(const struct dm_stats *dms, uint64_t group_id);
+
+/*
+ * Return the number of bins in the histogram configuration for the
+ * specified region or zero if no histogram specification is configured.
+ * Valid following a dm_stats_list() or dm_stats_populate() operation.
+ */
+int dm_stats_get_region_nr_histogram_bins(const struct dm_stats *dms,
+ uint64_t region_id);
+
+/*
+ * Parse a histogram string with optional unit suffixes into a
+ * dm_histogram bounds description.
+ *
+ * A histogram string is a string of numbers "n1,n2,n3,..." that
+ * represent the boundaries of a histogram. The first and final bins
+ * have implicit lower and upper bounds of zero and infinity
+ * respectively and boundary values must occur in order of ascending
+ * magnitude. Unless a unit suffix is given all values are specified in
+ * nanoseconds.
+ *
+ * For example, if bounds_str="300,600,900", the region will be created
+ * with a histogram containing four bins. Each report will include four
+ * numbers a:b:c:d. a is the number of requests that took between 0 and
+ * 300ns to complete, b is the number of requests that took 300-600ns to
+ * complete, c is the number of requests that took 600-900ns to complete
+ * and d is the number of requests that took more than 900ns to
+ * complete.
+ *
+ * An optional unit suffix of 's', 'ms', 'us', or 'ns' may be used to
+ * specify units of seconds, miliseconds, microseconds, or nanoseconds:
+ *
+ * bounds_str="1ns,1us,1ms,1s"
+ * bounds_str="500us,1ms,1500us,2ms"
+ * bounds_str="200ms,400ms,600ms,800ms,1s"
+ *
+ * The smallest valid unit of time for a histogram specification depends
+ * on whether the region uses precise timestamps: for a region with the
+ * default milisecond precision the smallest possible histogram boundary
+ * magnitude is one milisecond: attempting to use a histogram with a
+ * boundary less than one milisecond when creating a region will cause
+ * the region to be created with the precise_timestamps feature enabled.
+ *
+ * On sucess a pointer to the struct dm_histogram representing the
+ * bounds values is returned, or NULL in the case of error. The returned
+ * pointer should be freed using dm_free() when no longer required.
+ */
+struct dm_histogram *dm_histogram_bounds_from_string(const char *bounds_str);
+
+/*
+ * Parse a zero terminated array of uint64_t into a dm_histogram bounds
+ * description.
+ *
+ * Each value in the array specifies the upper bound of a bin in the
+ * latency histogram in nanoseconds. Values must appear in ascending
+ * order of magnitude.
+ *
+ * The smallest valid unit of time for a histogram specification depends
+ * on whether the region uses precise timestamps: for a region with the
+ * default milisecond precision the smallest possible histogram boundary
+ * magnitude is one milisecond: attempting to use a histogram with a
+ * boundary less than one milisecond when creating a region will cause
+ * the region to be created with the precise_timestamps feature enabled.
+ */
+struct dm_histogram *dm_histogram_bounds_from_uint64(const uint64_t *bounds);
+
+/*
+ * Destroy the histogram bounds array obtained from a call to
+ * dm_histogram_bounds_from_string().
+ */
+void dm_histogram_bounds_destroy(struct dm_histogram *bounds);
+
+/*
+ * Destroy a dm_stats object and all associated regions, counter
+ * sets and histograms.
+ */
+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.
+ *
+ * The interval is used to calculate time-based metrics from the basic
+ * counter data: an interval must be set before calling any of the
+ * metric methods.
+ *
+ * For best accuracy the duration should be measured and updated at the
+ * 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.
+ */
+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);
+
+/*
+ * Retrieve the configured sampling interval in either nanoseconds or
+ * milliseconds.
+ */
+uint64_t dm_stats_get_sampling_interval_ns(const struct dm_stats *dms);
+uint64_t dm_stats_get_sampling_interval_ms(const 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.
+ *
+ * Use with caution! Most users of the library should set a valid,
+ * non-NULL program_id for every statistics region created. Failing to
+ * do so may result in confusing state when multiple programs are
+ * creating and managing statistics regions.
+ *
+ * All users of the library are encouraged to choose an unambiguous,
+ * unique program_id: this could be based on PID (for programs that
+ * create, report, and delete regions in a single process), session id,
+ * executable name, or some other distinguishing string.
+ *
+ * Use of the empty string as a program_id does not simplify use of the
+ * library or the command line tools and use of this value is strongly
+ * discouraged.
+ */
+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.
+ *
+ * All values are returned in units of 512b sectors.
+ */
+int dm_stats_get_region_start(const struct dm_stats *dms, uint64_t *start,
+ uint64_t region_id);
+
+int dm_stats_get_region_len(const struct dm_stats *dms, uint64_t *len,
+ uint64_t region_id);
+
+int dm_stats_get_region_area_len(const struct dm_stats *dms,
+ uint64_t *len, uint64_t region_id);
+
+/*
+ * Area properties: start, offset 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 of an area is a function of the area_id and the containing
+ * region's start and area length: it gives the absolute offset into the
+ * containing device of the beginning of the area.
+ *
+ * The offset expresses the area's relative offset into the current
+ * region. I.e. the area start minus the start offset of the containing
+ * region.
+ *
+ * All values are returned in units of 512b sectors.
+ */
+int dm_stats_get_area_start(const struct dm_stats *dms, uint64_t *start,
+ uint64_t region_id, uint64_t area_id);
+
+int dm_stats_get_area_offset(const struct dm_stats *dms, uint64_t *offset,
+ uint64_t region_id, uint64_t area_id);
+
+/*
+ * Retrieve program_id and user aux_data for a specific region.
+ *
+ * Only valid following a call to dm_stats_list().
+ */
+
+/*
+ * Retrieve program_id for the specified region.
+ *
+ * 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.
+ */
+const char *dm_stats_get_region_program_id(const struct dm_stats *dms,
+ uint64_t region_id);
+
+/*
+ * Retrieve user aux_data set for the specified region. This function
+ * will return any stored user aux_data as a string in the memory
+ * pointed to by the aux_data argument.
+ *
+ * Any library internal aux_data fields, such as DMS_GROUP descriptors,
+ * are stripped before the value is returned.
+ *
+ * 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.
+ */
+const char *dm_stats_get_region_aux_data(const struct dm_stats *dms,
+ uint64_t region_id);
+
+typedef enum {
+ DM_STATS_OBJECT_TYPE_NONE,
+ DM_STATS_OBJECT_TYPE_AREA,
+ DM_STATS_OBJECT_TYPE_REGION,
+ DM_STATS_OBJECT_TYPE_GROUP
+} dm_stats_obj_type_t;
+
+/*
+ * Statistics cursor
+ *
+ * A dm_stats handle maintains an optional cursor into the statistics
+ * tables that it stores. Iterators are provided to visit each region,
+ * area, or group in a handle and accessor methods are provided to
+ * obtain properties and values for the object at the current cursor
+ * position.
+ *
+ * Using the cursor simplifies walking all regions or groups when
+ * the tables are sparse (i.e. contains some present and some
+ * non-present region_id or group_id values either due to program_id
+ * filtering or the ordering of region and group creation and deletion).
+ *
+ * Simple macros are provided to visit each area, region, or group,
+ * contained in a handle and applications are encouraged to use these
+ * where possible.
+ */
+
+/*
+ * Walk flags are used to initialise a dm_stats handle's cursor control
+ * and to select region or group aggregation when calling a metric or
+ * counter property method with immediate group, region, and area ID
+ * values.
+ *
+ * Walk flags are stored in the uppermost word of a uint64_t so that
+ * a region_id or group_id may be encoded in the lower bits. This
+ * allows an aggregate region_id or group_id to be specified when
+ * retrieving counter or metric values.
+ *
+ * Flags may be ORred together when used to initialise a dm_stats_walk:
+ * the resulting walk will visit instance of each type specified by
+ * the flag combination.
+ */
+#define DM_STATS_WALK_AREA 0x1000000000000ULL
+#define DM_STATS_WALK_REGION 0x2000000000000ULL
+#define DM_STATS_WALK_GROUP 0x4000000000000ULL
+
+#define DM_STATS_WALK_ALL 0x7000000000000ULL
+#define DM_STATS_WALK_DEFAULT (DM_STATS_WALK_AREA | DM_STATS_WALK_REGION)
+
+/*
+ * Skip regions from a DM_STATS_WALK_REGION that contain only a single
+ * area: in this case the region's aggregate values are identical to
+ * the values of the single contained area. Setting this flag will
+ * suppress these duplicate entries during a dm_stats_walk_* with the
+ * DM_STATS_WALK_REGION flag set.
+ */
+#define DM_STATS_WALK_SKIP_SINGLE_AREA 0x8000000000000ULL
+
+/*
+ * Initialise the cursor control of a dm_stats handle for the specified
+ * walk type(s). Including a walk flag in the flags argument will cause
+ * any subsequent walk to visit that type of object (until the next
+ * call to dm_stats_walk_init()).
+ */
+int dm_stats_walk_init(struct dm_stats *dms, uint64_t flags);
+
+/*
+ * Set the cursor of a dm_stats handle to address the first present
+ * group, region, or area of the currently configured walk. 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. If the end of
+ * the region, area, or group tables is reached a subsequent call to
+ * dm_stats_walk_end() will return 1 and dm_stats_object_type() called
+ * on the location will return DM_STATS_OBJECT_TYPE_NONE,
+ */
+void dm_stats_walk_next(struct dm_stats *dms);
+
+/*
+ * Force the statistics cursor to advance to the next region. This will
+ * stop any in-progress area walk (by clearing DM_STATS_WALK_AREA) and
+ * advance the cursor to the next present region, the first present
+ * group (if DM_STATS_GROUP_WALK is set), or to the end. In this case a
+ * subsequent call to dm_stats_walk_end() will return 1 and a call to
+ * dm_stats_object_type() for the location will return
+ * DM_STATS_OBJECT_TYPE_NONE.
+ */
+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);
+
+/*
+ * Return the type of object at the location specified by 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 the
+ * corresponding region or area identifier will be taken from the
+ * current cursor location. If the cursor location or the value encoded
+ * by region_id and area_id indicates an aggregate region or group,
+ * this will be reflected in the value returned.
+ */
+dm_stats_obj_type_t dm_stats_object_type(const struct dm_stats *dms,
+ uint64_t region_id,
+ uint64_t area_id);
+
+/*
+ * Return the type of object at the current stats cursor location.
+ */
+dm_stats_obj_type_t dm_stats_current_object_type(const 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_init((dms), DM_STATS_WALK_REGION), \
+ 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_init((dms), DM_STATS_WALK_AREA), \
+ dm_stats_walk_start((dms)); \
+ !dm_stats_walk_end((dms)); dm_stats_walk_next((dms)))
+
+/*
+ * Iterate over the regions table visiting each group. Metric and
+ * counter methods will return values for the group.
+ *
+ * If the group table is empty or unpopulated the loop body will not
+ * be executed.
+ */
+#define dm_stats_foreach_group(dms) \
+for (dm_stats_walk_init((dms), DM_STATS_WALK_GROUP), \
+ 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) \
+do { \
+ dm_stats_walk_start((dms)); \
+ do
+
+/*
+ * Start a 'while' style loop or end a 'do..while' loop iterating over the
+ * regions contained in dm_stats handle 'dms'.
+ */
+#define dm_stats_walk_while(dms) \
+ while(!dm_stats_walk_end((dms))); \
+} while (0)
+
+/*
+ * 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(const struct dm_stats *dms);
+
+/*
+ * Retrieve the current values of the stats cursor.
+ */
+uint64_t dm_stats_get_current_region(const struct dm_stats *dms);
+uint64_t dm_stats_get_current_area(const 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.
+ */
+int dm_stats_get_current_region_start(const struct dm_stats *dms,
+ uint64_t *start);
+
+int dm_stats_get_current_region_len(const struct dm_stats *dms,
+ uint64_t *len);
+
+int dm_stats_get_current_region_area_len(const 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.
+ */
+int dm_stats_get_current_area_start(const struct dm_stats *dms,
+ uint64_t *start);
+
+int dm_stats_get_current_area_offset(const struct dm_stats *dms,
+ uint64_t *offset);
+
+int dm_stats_get_current_area_len(const 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(const struct dm_stats *dms);
+
+/*
+ * Return a pointer to the user aux_data string for the region at the
+ * current cursor location.
+ */
+const char *dm_stats_get_current_region_aux_data(const struct dm_stats *dms);
+
+/*
+ * Statistics groups and data aggregation.
+ */
+
+/*
+ * Create a new group in stats handle dms from the group descriptor
+ * passed in group. The group descriptor is a string containing a list
+ * of region_id values that will be included in the group. The first
+ * region_id found will be the group leader. Ranges of identifiers may
+ * be expressed as "M-N", where M and N are the start and end region_id
+ * values for the range.
+ */
+int dm_stats_create_group(struct dm_stats *dms, const char *group,
+ const char *alias, uint64_t *group_id);
+
+/*
+ * Remove the specified group_id. If the remove argument is zero the
+ * group will be removed but the regions that it contained will remain.
+ * If remove is non-zero then all regions that belong to the group will
+ * also be removed.
+ */
+int dm_stats_delete_group(struct dm_stats *dms, uint64_t group_id, int remove);
+
+/*
+ * Set an alias for this group or region. The alias will be returned
+ * instead of the normal dm-stats name for this region or group.
+ */
+int dm_stats_set_alias(struct dm_stats *dms, uint64_t group_id,
+ const char *alias);
+
+/*
+ * Returns a pointer to the currently configured alias for id, or the
+ * name of the dm device the handle is bound to if no alias has been
+ * set. The pointer will be freed automatically when a new alias is set
+ * or when the stats handle is cleared.
+ */
+const char *dm_stats_get_alias(const struct dm_stats *dms, uint64_t id);
+
+#define DM_STATS_GROUP_NONE UINT64_MAX
+/*
+ * Return the group_id that the specified region_id belongs to, or the
+ * special value DM_STATS_GROUP_NONE if the region does not belong
+ * to any group.
+ */
+uint64_t dm_stats_get_group_id(const struct dm_stats *dms, uint64_t region_id);
+
+/*
+ * Store a pointer to a string describing the regions that are members
+ * of the group specified by group_id in the memory pointed to by buf.
+ * The string is in the same format as the 'group' argument to
+ * dm_stats_create_group().
+ *
+ * The pointer does not need to be freed explicitly by the caller: it
+ * will become invalid following a subsequent dm_stats_list(),
+ * dm_stats_populate() or dm_stats_destroy() of the corresponding
+ * dm_stats handle.
+ */
+int dm_stats_get_group_descriptor(const struct dm_stats *dms,
+ uint64_t group_id, char **buf);
+
+/*
+ * Create regions that correspond to the extents of a file in the
+ * filesystem and optionally place them into a group.
+ *
+ * File descriptor fd must reference a regular file, open for reading,
+ * in a local file system that supports the FIEMAP ioctl, and that
+ * returns data describing the physical location of extents.
+ *
+ * The file descriptor can be closed by the caller following the call
+ * to dm_stats_create_regions_from_fd().
+ *
+ * Unless nogroup is non-zero the regions will be placed into a group
+ * and the group alias set to the value supplied (if alias is NULL no
+ * group alias will be assigned).
+ *
+ * On success the function returns a pointer to an array of uint64_t
+ * containing the IDs of the newly created regions. The region_id
+ * array is terminated by the value DM_STATS_REGION_NOT_PRESENT and
+ * should be freed using dm_free() when no longer required.
+ *
+ * On error NULL is returned.
+ *
+ * Following a call to dm_stats_create_regions_from_fd() the handle
+ * is guaranteed to be in a listed state, and to contain any region
+ * and group identifiers created by the operation.
+ *
+ * The group_id for the new group is equal to the region_id value in
+ * the first array element.
+ */
+uint64_t *dm_stats_create_regions_from_fd(struct dm_stats *dms, int fd,
+ int group, int precise,
+ struct dm_histogram *bounds,
+ const char *alias);
+/*
+ * Update a group of regions that correspond to the extents of a file
+ * in the filesystem, adding and removing regions to account for
+ * allocation changes in the underlying file.
+ *
+ * File descriptor fd must reference a regular file, open for reading,
+ * in a local file system that supports the FIEMAP ioctl, and that
+ * returns data describing the physical location of extents.
+ *
+ * The file descriptor can be closed by the caller following the call
+ * to dm_stats_update_regions_from_fd().
+ *
+ * On success the function returns a pointer to an array of uint64_t
+ * containing the IDs of the updated regions (including any existing
+ * regions that were not modified by the call).
+ *
+ * The region_id array is terminated by the special value
+ * DM_STATS_REGION_NOT_PRESENT and should be freed using dm_free()
+ * when no longer required.
+ *
+ * On error NULL is returned.
+ *
+ * Following a call to dm_stats_update_regions_from_fd() the handle
+ * is guaranteed to be in a listed state, and to contain any region
+ * and group identifiers created by the operation.
+ *
+ * This function cannot be used with file mapped regions that are
+ * not members of a group: either group the regions, or remove them
+ * and re-map them with dm_stats_create_regions_from_fd().
+ */
+uint64_t *dm_stats_update_regions_from_fd(struct dm_stats *dms, int fd,
+ uint64_t group_id);
+
+
+/*
+ * The file map monitoring daemon can monitor files in two distinct
+ * ways: the mode affects the behaviour of the daemon when a file
+ * under monitoring is renamed or unlinked, and the conditions which
+ * cause the daemon to terminate.
+ *
+ * In both modes, the daemon will always shut down when the group
+ * being monitored is deleted.
+ *
+ * Follow inode:
+ * The daemon follows the inode of the file, as it was at the time the
+ * daemon started. The file descriptor referencing the file is kept
+ * open at all times, and the daemon will exit when it detects that
+ * the file has been unlinked and it is the last holder of a reference
+ * to the file.
+ *
+ * This mode is useful if the file is expected to be renamed, or moved
+ * within the file system, while it is being monitored.
+ *
+ * Follow path:
+ * The daemon follows the path that was given on the daemon command
+ * line. The file descriptor referencing the file is re-opened on each
+ * iteration of the daemon, and the daemon will exit if no file exists
+ * at this location (a tolerance is allowed so that a brief delay
+ * between unlink() and creat() is permitted).
+ *
+ * This mode is useful if the file is updated by unlinking the original
+ * and placing a new file at the same path.
+ */
+
+typedef enum {
+ DM_FILEMAPD_FOLLOW_INODE,
+ DM_FILEMAPD_FOLLOW_PATH,
+ DM_FILEMAPD_FOLLOW_NONE
+} dm_filemapd_mode_t;
+
+/*
+ * Parse a string representation of a dmfilemapd mode.
+ *
+ * Returns a valid dm_filemapd_mode_t value on success, or
+ * DM_FILEMAPD_FOLLOW_NONE on error.
+ */
+dm_filemapd_mode_t dm_filemapd_mode_from_string(const char *mode_str);
+
+/*
+ * Start the dmfilemapd filemap monitoring daemon for the specified
+ * file descriptor, group, and file system path. The daemon will
+ * monitor the file for allocation changes, and when a change is
+ * detected, call dm_stats_update_regions_from_fd() to update the
+ * mapped regions for the file.
+ *
+ * The path provided to dm_stats_start_filemapd() must be an absolute
+ * path, and should reflect the path of 'fd' at the time that it was
+ * opened.
+ *
+ * The mode parameter controls the behaviour of the daemon when the
+ * file being monitored is unlinked or moved: see the comments for
+ * dm_filemapd_mode_t for a full description and possible values.
+ *
+ * The daemon can be stopped at any time by sending SIGTERM to the
+ * daemon pid.
+ */
+int dm_stats_start_filemapd(int fd, uint64_t group_id, const char *path,
+ dm_filemapd_mode_t mode, unsigned foreground,
+ unsigned verbose);
+
+/*
+ * Call this to actually run the ioctl.
+ */
+int dm_task_run(struct dm_task *dmt);
+
+/*
+ * The errno from the last device-mapper ioctl performed by dm_task_run.
+ */
+int dm_task_get_errno(struct dm_task *dmt);
+
+/*
+ * Call this to make or remove the device nodes associated with previously
+ * issued commands.
+ */
+void dm_task_update_nodes(void);
+
+/*
+ * Mangling support
+ *
+ * Character whitelist: 0-9, A-Z, a-z, #+-.:=@_
+ * HEX mangling format: \xNN, NN being the hex value of the character.
+ * (whitelist and format supported by udev)
+*/
+typedef enum {
+ DM_STRING_MANGLING_NONE, /* do not mangle at all */
+ DM_STRING_MANGLING_AUTO, /* mangle only if not already mangled with hex, error when mixed */
+ DM_STRING_MANGLING_HEX /* always mangle with hex encoding, no matter what the input is */
+} dm_string_mangling_t;
+
+/*
+ * Set/get mangling mode used for device-mapper names and uuids.
+ */
+int dm_set_name_mangling_mode(dm_string_mangling_t name_mangling);
+dm_string_mangling_t dm_get_name_mangling_mode(void);
+
+/*
+ * Get mangled/unmangled form of the device-mapper name or uuid
+ * irrespective of the global setting (set by dm_set_name_mangling_mode).
+ * The name or uuid returned needs to be freed after use by calling dm_free!
+ */
+char *dm_task_get_name_mangled(const struct dm_task *dmt);
+char *dm_task_get_name_unmangled(const struct dm_task *dmt);
+char *dm_task_get_uuid_mangled(const struct dm_task *dmt);
+char *dm_task_get_uuid_unmangled(const struct dm_task *dmt);
+
+/*
+ * Configure the device-mapper directory
+ */
+int dm_set_dev_dir(const char *dir);
+const char *dm_dir(void);
+
+/*
+ * Configure sysfs directory, /sys by default
+ */
+int dm_set_sysfs_dir(const char *dir);
+const char *dm_sysfs_dir(void);
+
+/*
+ * Configure default UUID prefix string.
+ * Conventionally this is a short capitalised prefix indicating the subsystem
+ * that is managing the devices, e.g. "LVM-" or "MPATH-".
+ * To support stacks of devices from different subsystems, recursive functions
+ * stop recursing if they reach a device with a different prefix.
+ */
+int dm_set_uuid_prefix(const char *uuid_prefix);
+const char *dm_uuid_prefix(void);
+
+/*
+ * Determine whether a major number belongs to device-mapper or not.
+ */
+int dm_is_dm_major(uint32_t major);
+
+/*
+ * Get associated device name for given major and minor number by reading
+ * the sysfs content. If this is a dm device, get associated dm name, the one
+ * that appears in /dev/mapper. DM names could be resolved this way only if
+ * kernel used >= 2.6.29, kernel name is found otherwise (e.g. dm-0).
+ * If prefer_kernel_name is set, the kernel name is always preferred over
+ * device-mapper name for dm devices no matter what the kernel version is.
+ * For non-dm devices, we always get associated kernel name, e.g sda, md0 etc.
+ * Returns 0 on error or if sysfs is not used (or configured incorrectly),
+ * otherwise returns 1 and the supplied buffer holds the device name.
+ */
+int dm_device_get_name(uint32_t major, uint32_t minor,
+ int prefer_kernel_name,
+ char *buf, size_t buf_size);
+
+/*
+ * Determine whether a device has any holders (devices
+ * using this device). If sysfs is not used (or configured
+ * incorrectly), returns 0.
+ */
+int dm_device_has_holders(uint32_t major, uint32_t minor);
+
+/*
+ * Determine whether a device contains mounted filesystem.
+ * If sysfs is not used (or configured incorrectly), returns 0.
+ */
+int dm_device_has_mounted_fs(uint32_t major, uint32_t minor);
+
+
+/*
+ * Callback is invoked for individal mountinfo lines,
+ * minor, major and mount target are parsed and unmangled.
+ */
+typedef int (*dm_mountinfo_line_callback_fn) (char *line, unsigned maj, unsigned min,
+ char *target, void *cb_data);
+
+/*
+ * Read all lines from /proc/self/mountinfo,
+ * for each line calls read_fn callback.
+ */
+int dm_mountinfo_read(dm_mountinfo_line_callback_fn read_fn, void *cb_data);
+
+/*
+ * Initialise library
+ */
+void dm_lib_init(void) __attribute__((constructor));
+
+/*
+ * Release library resources
+ */
+void dm_lib_release(void);
+void dm_lib_exit(void) __attribute__((destructor));
+
+/* An optimisation for clients making repeated calls involving dm ioctls */
+void dm_hold_control_dev(int hold_open);
+
+/*
+ * Use NULL for all devices.
+ */
+int dm_mknodes(const char *name);
+int dm_driver_version(char *version, size_t size);
+
+/******************************************************
+ * Functions to build and manipulate trees of devices *
+ ******************************************************/
+struct dm_tree;
+struct dm_tree_node;
+
+/*
+ * Initialise an empty dependency tree.
+ *
+ * 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
+ * parents/children.
+ */
+struct dm_tree *dm_tree_create(void);
+void dm_tree_free(struct dm_tree *tree);
+
+/*
+ * List of suffixes to be ignored when matching uuids against existing devices.
+ */
+void dm_tree_set_optional_uuid_suffixes(struct dm_tree *dtree, const char **optional_uuid_suffixes);
+
+/*
+ * Add nodes to the tree for a given device and all the devices it uses.
+ */
+int dm_tree_add_dev(struct dm_tree *tree, uint32_t major, uint32_t minor);
+int dm_tree_add_dev_with_udev_flags(struct dm_tree *tree, uint32_t major,
+ uint32_t minor, uint16_t udev_flags);
+
+/*
+ * Add a new node to the tree if it doesn't already exist.
+ */
+struct dm_tree_node *dm_tree_add_new_dev(struct dm_tree *tree,
+ const char *name,
+ const char *uuid,
+ uint32_t major, uint32_t minor,
+ int read_only,
+ int clear_inactive,
+ void *context);
+struct dm_tree_node *dm_tree_add_new_dev_with_udev_flags(struct dm_tree *tree,
+ const char *name,
+ const char *uuid,
+ uint32_t major,
+ uint32_t minor,
+ int read_only,
+ int clear_inactive,
+ void *context,
+ uint16_t udev_flags);
+
+/*
+ * Search for a node in the tree.
+ * Set major and minor to 0 or uuid to NULL to get the root node.
+ */
+struct dm_tree_node *dm_tree_find_node(struct dm_tree *tree,
+ uint32_t major,
+ uint32_t minor);
+struct dm_tree_node *dm_tree_find_node_by_uuid(struct dm_tree *tree,
+ const char *uuid);
+
+/*
+ * Use this to walk through all children of a given node.
+ * Set handle to NULL in first call.
+ * Returns NULL after the last child.
+ * Set inverted to use inverted tree.
+ */
+struct dm_tree_node *dm_tree_next_child(void **handle,
+ const struct dm_tree_node *parent,
+ uint32_t inverted);
+
+/*
+ * Get properties of a node.
+ */
+const char *dm_tree_node_get_name(const struct dm_tree_node *node);
+const char *dm_tree_node_get_uuid(const struct dm_tree_node *node);
+const struct dm_info *dm_tree_node_get_info(const struct dm_tree_node *node);
+void *dm_tree_node_get_context(const struct dm_tree_node *node);
+/*
+ * Returns 0 when node size and its children is unchanged.
+ * Returns 1 when node or any of its children has increased size.
+ * Rerurns -1 when node or any of its children has reduced size.
+ */
+int dm_tree_node_size_changed(const struct dm_tree_node *dnode);
+
+/*
+ * Returns the number of children of the given node (excluding the root node).
+ * Set inverted for the number of parents.
+ */
+int dm_tree_node_num_children(const struct dm_tree_node *node, uint32_t inverted);
+
+/*
+ * Deactivate a device plus all dependencies.
+ * Ignores devices that don't have a uuid starting with uuid_prefix.
+ */
+int dm_tree_deactivate_children(struct dm_tree_node *dnode,
+ const char *uuid_prefix,
+ size_t uuid_prefix_len);
+/*
+ * Preload/create a device plus all dependencies.
+ * Ignores devices that don't have a uuid starting with uuid_prefix.
+ */
+int dm_tree_preload_children(struct dm_tree_node *dnode,
+ const char *uuid_prefix,
+ size_t uuid_prefix_len);
+
+/*
+ * Resume a device plus all dependencies.
+ * Ignores devices that don't have a uuid starting with uuid_prefix.
+ */
+int dm_tree_activate_children(struct dm_tree_node *dnode,
+ const char *uuid_prefix,
+ size_t uuid_prefix_len);
+
+/*
+ * Suspend a device plus all dependencies.
+ * Ignores devices that don't have a uuid starting with uuid_prefix.
+ */
+int dm_tree_suspend_children(struct dm_tree_node *dnode,
+ const char *uuid_prefix,
+ size_t uuid_prefix_len);
+
+/*
+ * 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);
+
+/*
+ * Set the 'noflush' flag when suspending devices.
+ * If the kernel supports it, instead of erroring outstanding I/O that
+ * cannot be completed, the I/O is queued and resubmitted when the
+ * device is resumed. This affects multipath devices when all paths
+ * have failed and queue_if_no_path is set, and mirror devices when
+ * block_on_error is set and the mirror log has failed.
+ */
+void dm_tree_use_no_flush_suspend(struct dm_tree_node *dnode);
+
+/*
+ * Retry removal of each device if not successful.
+ */
+void dm_tree_retry_remove(struct dm_tree_node *dnode);
+
+/*
+ * Is the uuid prefix present in the tree?
+ * Only returns 0 if every node was checked successfully.
+ * Returns 1 if the tree walk has to be aborted.
+ */
+int dm_tree_children_use_uuid(struct dm_tree_node *dnode,
+ const char *uuid_prefix,
+ size_t uuid_prefix_len);
+
+/*
+ * Construct tables for new nodes before activating them.
+ */
+int dm_tree_node_add_snapshot_origin_target(struct dm_tree_node *dnode,
+ uint64_t size,
+ const char *origin_uuid);
+int dm_tree_node_add_snapshot_target(struct dm_tree_node *node,
+ uint64_t size,
+ const char *origin_uuid,
+ const char *cow_uuid,
+ int persistent,
+ uint32_t chunk_size);
+int dm_tree_node_add_snapshot_merge_target(struct dm_tree_node *node,
+ uint64_t size,
+ const char *origin_uuid,
+ const char *cow_uuid,
+ const char *merge_uuid,
+ uint32_t chunk_size);
+int dm_tree_node_add_error_target(struct dm_tree_node *node,
+ uint64_t size);
+int dm_tree_node_add_zero_target(struct dm_tree_node *node,
+ uint64_t size);
+int dm_tree_node_add_linear_target(struct dm_tree_node *node,
+ uint64_t size);
+int dm_tree_node_add_striped_target(struct dm_tree_node *node,
+ uint64_t size,
+ uint32_t stripe_size);
+
+#define DM_CRYPT_IV_DEFAULT UINT64_C(-1) /* iv_offset == seg offset */
+/*
+ * Function accepts one string in cipher specification
+ * (chainmode and iv should be NULL because included in cipher string)
+ * or
+ * separate arguments which will be joined to "cipher-chainmode-iv"
+ */
+int dm_tree_node_add_crypt_target(struct dm_tree_node *node,
+ uint64_t size,
+ const char *cipher,
+ const char *chainmode,
+ const char *iv,
+ uint64_t iv_offset,
+ 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 */
+#define DM_BLOCK_ON_ERROR 0x00000004 /* On error, suspend I/O */
+#define DM_CORELOG 0x00000008 /* In-memory log */
+
+int dm_tree_node_add_mirror_target_log(struct dm_tree_node *node,
+ uint32_t region_size,
+ unsigned clustered,
+ const char *log_uuid,
+ unsigned area_count,
+ uint32_t flags);
+
+int dm_tree_node_add_raid_target(struct dm_tree_node *node,
+ uint64_t size,
+ const char *raid_type,
+ uint32_t region_size,
+ uint32_t stripe_size,
+ uint64_t rebuilds,
+ uint64_t flags);
+
+/*
+ * Defines below are based on kernel's dm-cache.c defines
+ * DM_CACHE_MIN_DATA_BLOCK_SIZE (32 * 1024 >> SECTOR_SHIFT)
+ * DM_CACHE_MAX_DATA_BLOCK_SIZE (1024 * 1024 * 1024 >> SECTOR_SHIFT)
+ */
+#define DM_CACHE_MIN_DATA_BLOCK_SIZE (UINT32_C(64))
+#define DM_CACHE_MAX_DATA_BLOCK_SIZE (UINT32_C(2097152))
+/*
+ * Max supported size for cache pool metadata device.
+ * Limitation is hardcoded into the kernel and bigger device sizes
+ * are not accepted.
+ *
+ * Limit defined in drivers/md/dm-cache-metadata.h
+ */
+#define DM_CACHE_METADATA_MAX_SECTORS DM_THIN_METADATA_MAX_SECTORS
+
+/*
+ * Define number of elements in rebuild and writemostly arrays
+ * 'of struct dm_tree_node_raid_params'.
+ */
+
+struct dm_tree_node_raid_params {
+ const char *raid_type;
+
+ uint32_t stripes;
+ uint32_t mirrors;
+ uint32_t region_size;
+ uint32_t stripe_size;
+
+ /*
+ * 'rebuilds' and 'writemostly' are bitfields that signify
+ * which devices in the array are to be rebuilt or marked
+ * writemostly. The kernel supports up to 253 legs.
+ * We limit ourselves by choosing a lower value
+ * for DEFAULT_RAID{1}_MAX_IMAGES in defaults.h.
+ */
+ uint64_t rebuilds;
+ uint64_t writemostly;
+ uint32_t writebehind; /* I/Os (kernel default COUNTER_MAX / 2) */
+ uint32_t sync_daemon_sleep; /* ms (kernel default = 5sec) */
+ uint32_t max_recovery_rate; /* kB/sec/disk */
+ uint32_t min_recovery_rate; /* kB/sec/disk */
+ uint32_t stripe_cache; /* sectors */
+
+ uint64_t flags; /* [no]sync */
+ uint32_t reserved2;
+};
+
+/*
+ * Version 2 of above node raid params struct to keeep API compatibility.
+ *
+ * Extended for more than 64 legs (max 253 in the MD kernel runtime!),
+ * delta_disks for disk add/remove reshaping,
+ * data_offset for out-of-place reshaping
+ * and data_copies for odd number of raid10 legs.
+ */
+#define RAID_BITMAP_SIZE 4 /* 4 * 64 bit elements in rebuilds/writemostly arrays */
+struct dm_tree_node_raid_params_v2 {
+ const char *raid_type;
+
+ uint32_t stripes;
+ uint32_t mirrors;
+ uint32_t region_size;
+ uint32_t stripe_size;
+
+ int delta_disks; /* +/- number of disks to add/remove (reshaping) */
+ int data_offset; /* data offset to set (out-of-place reshaping) */
+
+ /*
+ * 'rebuilds' and 'writemostly' are bitfields that signify
+ * which devices in the array are to be rebuilt or marked
+ * writemostly. The kernel supports up to 253 legs.
+ * We limit ourselvs by choosing a lower value
+ * for DEFAULT_RAID_MAX_IMAGES.
+ */
+ uint64_t rebuilds[RAID_BITMAP_SIZE];
+ uint64_t writemostly[RAID_BITMAP_SIZE];
+ uint32_t writebehind; /* I/Os (kernel default COUNTER_MAX / 2) */
+ uint32_t data_copies; /* RAID # of data copies */
+ uint32_t sync_daemon_sleep; /* ms (kernel default = 5sec) */
+ uint32_t max_recovery_rate; /* kB/sec/disk */
+ uint32_t min_recovery_rate; /* kB/sec/disk */
+ uint32_t stripe_cache; /* sectors */
+
+ uint64_t flags; /* [no]sync */
+};
+
+int dm_tree_node_add_raid_target_with_params(struct dm_tree_node *node,
+ uint64_t size,
+ const struct dm_tree_node_raid_params *p);
+
+/* Version 2 API function taking dm_tree_node_raid_params_v2 for aforementioned extensions. */
+int dm_tree_node_add_raid_target_with_params_v2(struct dm_tree_node *node,
+ uint64_t size,
+ const struct dm_tree_node_raid_params_v2 *p);
+
+/* Cache feature_flags */
+#define DM_CACHE_FEATURE_WRITEBACK 0x00000001
+#define DM_CACHE_FEATURE_WRITETHROUGH 0x00000002
+#define DM_CACHE_FEATURE_PASSTHROUGH 0x00000004
+#define DM_CACHE_FEATURE_METADATA2 0x00000008 /* cache v1.10 */
+
+struct dm_config_node;
+/*
+ * Use for passing cache policy and all its args e.g.:
+ *
+ * policy_settings {
+ * migration_threshold=2048
+ * sequention_threashold=100
+ * ...
+ * }
+ *
+ * For policy without any parameters use NULL.
+ */
+int dm_tree_node_add_cache_target(struct dm_tree_node *node,
+ uint64_t size,
+ uint64_t feature_flags, /* DM_CACHE_FEATURE_* */
+ const char *metadata_uuid,
+ const char *data_uuid,
+ const char *origin_uuid,
+ const char *policy_name,
+ const struct dm_config_node *policy_settings,
+ uint32_t data_block_size);
+
+/*
+ * FIXME Add individual cache policy pairs <key> = value, like:
+ * int dm_tree_node_add_cache_policy_arg(struct dm_tree_node *dnode,
+ * const char *key, uint64_t value);
+ */
+
+/*
+ * Replicator operation mode
+ * Note: API for Replicator is not yet stable
+ */
+typedef enum {
+ DM_REPLICATOR_SYNC, /* Synchronous replication */
+ DM_REPLICATOR_ASYNC_WARN, /* Warn if async replicator is slow */
+ DM_REPLICATOR_ASYNC_STALL, /* Stall replicator if not fast enough */
+ DM_REPLICATOR_ASYNC_DROP, /* Drop sites out of sync */
+ DM_REPLICATOR_ASYNC_FAIL, /* Fail replicator if slow */
+ NUM_DM_REPLICATOR_MODES
+} dm_replicator_mode_t;
+
+int dm_tree_node_add_replicator_target(struct dm_tree_node *node,
+ uint64_t size,
+ const char *rlog_uuid,
+ const char *rlog_type,
+ unsigned rsite_index,
+ dm_replicator_mode_t mode,
+ uint32_t async_timeout,
+ uint64_t fall_behind_data,
+ uint32_t fall_behind_ios);
+
+int dm_tree_node_add_replicator_dev_target(struct dm_tree_node *node,
+ uint64_t size,
+ const char *replicator_uuid, /* Replicator control device */
+ uint64_t rdevice_index,
+ const char *rdev_uuid, /* Rimage device name/uuid */
+ unsigned rsite_index,
+ const char *slog_uuid,
+ uint32_t slog_flags, /* Mirror log flags */
+ uint32_t slog_region_size);
+/* End of Replicator API */
+
+/*
+ * FIXME: Defines bellow are based on kernel's dm-thin.c defines
+ * DATA_DEV_BLOCK_SIZE_MIN_SECTORS (64 * 1024 >> SECTOR_SHIFT)
+ * DATA_DEV_BLOCK_SIZE_MAX_SECTORS (1024 * 1024 * 1024 >> SECTOR_SHIFT)
+ */
+#define DM_THIN_MIN_DATA_BLOCK_SIZE (UINT32_C(128))
+#define DM_THIN_MAX_DATA_BLOCK_SIZE (UINT32_C(2097152))
+/*
+ * Max supported size for thin pool metadata device (17112760320 bytes)
+ * Limitation is hardcoded into the kernel and bigger device size
+ * is not accepted.
+ * drivers/md/dm-thin-metadata.h THIN_METADATA_MAX_SECTORS
+ */
+#define DM_THIN_MAX_METADATA_SIZE (UINT64_C(255) * (1 << 14) * (4096 / (1 << 9)) - 256 * 1024)
+
+int dm_tree_node_add_thin_pool_target(struct dm_tree_node *node,
+ uint64_t size,
+ uint64_t transaction_id,
+ const char *metadata_uuid,
+ const char *pool_uuid,
+ uint32_t data_block_size,
+ uint64_t low_water_mark,
+ unsigned skip_block_zeroing);
+
+/* Supported messages for thin provision target */
+typedef enum {
+ DM_THIN_MESSAGE_CREATE_SNAP, /* device_id, origin_id */
+ DM_THIN_MESSAGE_CREATE_THIN, /* device_id */
+ DM_THIN_MESSAGE_DELETE, /* device_id */
+ DM_THIN_MESSAGE_SET_TRANSACTION_ID, /* current_id, new_id */
+ DM_THIN_MESSAGE_RESERVE_METADATA_SNAP, /* target version >= 1.1 */
+ DM_THIN_MESSAGE_RELEASE_METADATA_SNAP, /* target version >= 1.1 */
+} dm_thin_message_t;
+
+int dm_tree_node_add_thin_pool_message(struct dm_tree_node *node,
+ dm_thin_message_t type,
+ uint64_t id1, uint64_t id2);
+
+/*
+ * Set thin pool discard features
+ * ignore - Disable support for discards
+ * no_passdown - Don't pass discards down to underlying data device,
+ * just remove the mapping
+ * Feature is available since version 1.1 of the thin target.
+ */
+int dm_tree_node_set_thin_pool_discard(struct dm_tree_node *node,
+ unsigned ignore,
+ unsigned no_passdown);
+/*
+ * Set error if no space, instead of queueing for thin pool.
+ */
+int dm_tree_node_set_thin_pool_error_if_no_space(struct dm_tree_node *node,
+ unsigned error_if_no_space);
+/* Start thin pool with metadata in read-only mode */
+int dm_tree_node_set_thin_pool_read_only(struct dm_tree_node *node,
+ unsigned read_only);
+/*
+ * FIXME: Defines bellow are based on kernel's dm-thin.c defines
+ * MAX_DEV_ID ((1 << 24) - 1)
+ */
+#define DM_THIN_MAX_DEVICE_ID (UINT32_C((1 << 24) - 1))
+int dm_tree_node_add_thin_target(struct dm_tree_node *node,
+ uint64_t size,
+ const char *pool_uuid,
+ uint32_t device_id);
+
+int dm_tree_node_set_thin_external_origin(struct dm_tree_node *node,
+ const char *external_uuid);
+
+void dm_tree_node_set_udev_flags(struct dm_tree_node *node, uint16_t udev_flags);
+
+void dm_tree_node_set_presuspend_node(struct dm_tree_node *node,
+ struct dm_tree_node *presuspend_node);
+
+int dm_tree_node_add_target_area(struct dm_tree_node *node,
+ const char *dev_name,
+ const char *dlid,
+ uint64_t offset);
+
+/*
+ * Only for temporarily-missing raid devices where changes are tracked.
+ */
+int dm_tree_node_add_null_area(struct dm_tree_node *node, uint64_t offset);
+
+/*
+ * Set readahead (in sectors) after loading the node.
+ */
+void dm_tree_node_set_read_ahead(struct dm_tree_node *dnode,
+ uint32_t read_ahead,
+ uint32_t read_ahead_flags);
+
+/*
+ * Set node callback hook before de/activation.
+ * Callback is called before 'activation' of node for activation tree,
+ * or 'deactivation' of node for deactivation tree.
+ */
+typedef enum {
+ DM_NODE_CALLBACK_PRELOADED, /* Node has preload deps */
+ DM_NODE_CALLBACK_DEACTIVATED, /* Node is deactivated */
+} dm_node_callback_t;
+typedef int (*dm_node_callback_fn) (struct dm_tree_node *node,
+ dm_node_callback_t type, void *cb_data);
+void dm_tree_node_set_callback(struct dm_tree_node *node,
+ dm_node_callback_fn cb, void *cb_data);
+
+void dm_tree_set_cookie(struct dm_tree_node *node, uint32_t cookie);
+uint32_t dm_tree_get_cookie(struct dm_tree_node *node);
+
+/*****************************************************************************
+ * Library functions
+ *****************************************************************************/
+
+/*******************
+ * Memory management
+ *******************/
+
+/*
+ * Never use these functions directly - use the macros following instead.
+ */
+void *dm_malloc_wrapper(size_t s, const char *file, int line)
+ __attribute__((__malloc__)) __attribute__((__warn_unused_result__));
+void *dm_malloc_aligned_wrapper(size_t s, size_t a, const char *file, int line)
+ __attribute__((__malloc__)) __attribute__((__warn_unused_result__));
+void *dm_zalloc_wrapper(size_t s, const char *file, int line)
+ __attribute__((__malloc__)) __attribute__((__warn_unused_result__));
+void *dm_realloc_wrapper(void *p, unsigned int s, const char *file, int line)
+ __attribute__((__warn_unused_result__));
+void dm_free_wrapper(void *ptr);
+char *dm_strdup_wrapper(const char *s, const char *file, int line)
+ __attribute__((__warn_unused_result__));
+int dm_dump_memory_wrapper(void);
+void dm_bounds_check_wrapper(void);
+
+#define dm_malloc(s) dm_malloc_wrapper((s), __FILE__, __LINE__)
+#define dm_malloc_aligned(s, a) dm_malloc_aligned_wrapper((s), (a), __FILE__, __LINE__)
+#define dm_zalloc(s) dm_zalloc_wrapper((s), __FILE__, __LINE__)
+#define dm_strdup(s) dm_strdup_wrapper((s), __FILE__, __LINE__)
+#define dm_free(p) dm_free_wrapper(p)
+#define dm_realloc(p, s) dm_realloc_wrapper((p), (s), __FILE__, __LINE__)
+#define dm_dump_memory() dm_dump_memory_wrapper()
+#define dm_bounds_check() dm_bounds_check_wrapper()
+
+/*
+ * The pool allocator is useful when you are going to allocate
+ * lots of memory, use the memory for a bit, and then free the
+ * memory in one go. A surprising amount of code has this usage
+ * profile.
+ *
+ * You should think of the pool as an infinite, contiguous chunk
+ * of memory. The front of this chunk of memory contains
+ * allocated objects, the second half is free. dm_pool_alloc grabs
+ * the next 'size' bytes from the free half, in effect moving it
+ * into the allocated half. This operation is very efficient.
+ *
+ * dm_pool_free frees the allocated object *and* all objects
+ * allocated after it. It is important to note this semantic
+ * difference from malloc/free. This is also extremely
+ * efficient, since a single dm_pool_free can dispose of a large
+ * complex object.
+ *
+ * dm_pool_destroy frees all allocated memory.
+ *
+ * eg, If you are building a binary tree in your program, and
+ * know that you are only ever going to insert into your tree,
+ * and not delete (eg, maintaining a symbol table for a
+ * compiler). You can create yourself a pool, allocate the nodes
+ * from it, and when the tree becomes redundant call dm_pool_destroy
+ * (no nasty iterating through the tree to free nodes).
+ *
+ * eg, On the other hand if you wanted to repeatedly insert and
+ * remove objects into the tree, you would be better off
+ * allocating the nodes from a free list; you cannot free a
+ * single arbitrary node with pool.
+ */
+
+struct dm_pool;
+
+/* constructor and destructor */
+struct dm_pool *dm_pool_create(const char *name, size_t chunk_hint)
+ __attribute__((__warn_unused_result__));
+void dm_pool_destroy(struct dm_pool *p);
+
+/* simple allocation/free routines */
+void *dm_pool_alloc(struct dm_pool *p, size_t s)
+ __attribute__((__warn_unused_result__));
+void *dm_pool_alloc_aligned(struct dm_pool *p, size_t s, unsigned alignment)
+ __attribute__((__warn_unused_result__));
+void dm_pool_empty(struct dm_pool *p);
+void dm_pool_free(struct dm_pool *p, void *ptr);
+
+/*
+ * To aid debugging, a pool can be locked. Any modifications made
+ * to the content of the pool while it is locked can be detected.
+ * Default compilation is using a crc checksum to notice modifications.
+ * The pool locking is using the mprotect with the compilation flag
+ * DEBUG_ENFORCE_POOL_LOCKING to enforce the memory protection.
+ */
+/* query pool lock status */
+int dm_pool_locked(struct dm_pool *p);
+/* mark pool as locked */
+int dm_pool_lock(struct dm_pool *p, int crc)
+ __attribute__((__warn_unused_result__));
+/* mark pool as unlocked */
+int dm_pool_unlock(struct dm_pool *p, int crc)
+ __attribute__((__warn_unused_result__));
+
+/*
+ * Object building routines:
+ *
+ * These allow you to 'grow' an object, useful for
+ * building strings, or filling in dynamic
+ * arrays.
+ *
+ * It's probably best explained with an example:
+ *
+ * char *build_string(struct dm_pool *mem)
+ * {
+ * int i;
+ * char buffer[16];
+ *
+ * if (!dm_pool_begin_object(mem, 128))
+ * return NULL;
+ *
+ * for (i = 0; i < 50; i++) {
+ * snprintf(buffer, sizeof(buffer), "%d, ", i);
+ * if (!dm_pool_grow_object(mem, buffer, 0))
+ * goto bad;
+ * }
+ *
+ * // add null
+ * if (!dm_pool_grow_object(mem, "\0", 1))
+ * goto bad;
+ *
+ * return dm_pool_end_object(mem);
+ *
+ * bad:
+ *
+ * dm_pool_abandon_object(mem);
+ * return NULL;
+ *}
+ *
+ * So start an object by calling dm_pool_begin_object
+ * with a guess at the final object size - if in
+ * doubt make the guess too small.
+ *
+ * Then append chunks of data to your object with
+ * dm_pool_grow_object. Finally get your object with
+ * a call to dm_pool_end_object.
+ *
+ * Setting delta to 0 means it will use strlen(extra).
+ */
+int dm_pool_begin_object(struct dm_pool *p, size_t hint);
+int dm_pool_grow_object(struct dm_pool *p, const void *extra, size_t delta);
+void *dm_pool_end_object(struct dm_pool *p);
+void dm_pool_abandon_object(struct dm_pool *p);
+
+/* utilities */
+char *dm_pool_strdup(struct dm_pool *p, const char *str)
+ __attribute__((__warn_unused_result__));
+char *dm_pool_strndup(struct dm_pool *p, const char *str, size_t n)
+ __attribute__((__warn_unused_result__));
+void *dm_pool_zalloc(struct dm_pool *p, size_t s)
+ __attribute__((__warn_unused_result__));
+
+/******************
+ * bitset functions
+ ******************/
+
+typedef uint32_t *dm_bitset_t;
+
+dm_bitset_t dm_bitset_create(struct dm_pool *mem, unsigned num_bits);
+void dm_bitset_destroy(dm_bitset_t bs);
+
+int dm_bitset_equal(dm_bitset_t in1, dm_bitset_t in2);
+
+void dm_bit_and(dm_bitset_t out, dm_bitset_t in1, dm_bitset_t in2);
+void dm_bit_union(dm_bitset_t out, dm_bitset_t in1, dm_bitset_t in2);
+int dm_bit_get_first(dm_bitset_t bs);
+int dm_bit_get_next(dm_bitset_t bs, int last_bit);
+int dm_bit_get_last(dm_bitset_t bs);
+int dm_bit_get_prev(dm_bitset_t bs, int last_bit);
+
+#define DM_BITS_PER_INT (sizeof(int) * CHAR_BIT)
+
+#define dm_bit(bs, i) \
+ ((bs)[((i) / DM_BITS_PER_INT) + 1] & (0x1 << ((i) & (DM_BITS_PER_INT - 1))))
+
+#define dm_bit_set(bs, i) \
+ ((bs)[((i) / DM_BITS_PER_INT) + 1] |= (0x1 << ((i) & (DM_BITS_PER_INT - 1))))
+
+#define dm_bit_clear(bs, i) \
+ ((bs)[((i) / DM_BITS_PER_INT) + 1] &= ~(0x1 << ((i) & (DM_BITS_PER_INT - 1))))
+
+#define dm_bit_set_all(bs) \
+ memset((bs) + 1, -1, ((*(bs) / DM_BITS_PER_INT) + 1) * sizeof(int))
+
+#define dm_bit_clear_all(bs) \
+ memset((bs) + 1, 0, ((*(bs) / DM_BITS_PER_INT) + 1) * sizeof(int))
+
+#define dm_bit_copy(bs1, bs2) \
+ memcpy((bs1) + 1, (bs2) + 1, ((*(bs2) / DM_BITS_PER_INT) + 1) * sizeof(int))
+
+/*
+ * Parse a string representation of a bitset into a dm_bitset_t. The
+ * notation used is identical to the kernel bitmap parser (cpuset etc.)
+ * and supports both lists ("1,2,3") and ranges ("1-2,5-8"). If the mem
+ * parameter is NULL memory for the bitset will be allocated using
+ * dm_malloc(). Otherwise the bitset will be allocated using the supplied
+ * dm_pool.
+ */
+dm_bitset_t dm_bitset_parse_list(const char *str, struct dm_pool *mem,
+ size_t min_num_bits);
+
+/* Returns number of set bits */
+static inline unsigned hweight32(uint32_t i)
+{
+ unsigned r = (i & 0x55555555) + ((i >> 1) & 0x55555555);
+
+ r = (r & 0x33333333) + ((r >> 2) & 0x33333333);
+ r = (r & 0x0F0F0F0F) + ((r >> 4) & 0x0F0F0F0F);
+ r = (r & 0x00FF00FF) + ((r >> 8) & 0x00FF00FF);
+ return (r & 0x0000FFFF) + ((r >> 16) & 0x0000FFFF);
+}
+
+/****************
+ * hash functions
+ ****************/
+
+struct dm_hash_table;
+struct dm_hash_node;
+
+typedef void (*dm_hash_iterate_fn) (void *data);
+
+struct dm_hash_table *dm_hash_create(unsigned size_hint)
+ __attribute__((__warn_unused_result__));
+void dm_hash_destroy(struct dm_hash_table *t);
+void dm_hash_wipe(struct dm_hash_table *t);
+
+void *dm_hash_lookup(struct dm_hash_table *t, const char *key);
+int dm_hash_insert(struct dm_hash_table *t, const char *key, void *data);
+void dm_hash_remove(struct dm_hash_table *t, const char *key);
+
+void *dm_hash_lookup_binary(struct dm_hash_table *t, const void *key, uint32_t len);
+int dm_hash_insert_binary(struct dm_hash_table *t, const void *key, uint32_t len,
+ void *data);
+void dm_hash_remove_binary(struct dm_hash_table *t, const void *key, uint32_t len);
+
+unsigned dm_hash_get_num_entries(struct dm_hash_table *t);
+void dm_hash_iter(struct dm_hash_table *t, dm_hash_iterate_fn f);
+
+char *dm_hash_get_key(struct dm_hash_table *t, struct dm_hash_node *n);
+void *dm_hash_get_data(struct dm_hash_table *t, struct dm_hash_node *n);
+struct dm_hash_node *dm_hash_get_first(struct dm_hash_table *t);
+struct dm_hash_node *dm_hash_get_next(struct dm_hash_table *t, struct dm_hash_node *n);
+
+/*
+ * dm_hash_insert() replaces the value of an existing
+ * entry with a matching key if one exists. Otherwise
+ * it adds a new entry.
+ *
+ * dm_hash_insert_with_val() inserts a new entry if
+ * another entry with the same key already exists.
+ * val_len is the size of the data being inserted.
+ *
+ * If two entries with the same key exist,
+ * (added using dm_hash_insert_allow_multiple), then:
+ * . dm_hash_lookup() returns the first one it finds, and
+ * dm_hash_lookup_with_val() returns the one with a matching
+ * val_len/val.
+ * . dm_hash_remove() removes the first one it finds, and
+ * dm_hash_remove_with_val() removes the one with a matching
+ * val_len/val.
+ *
+ * If a single entry with a given key exists, and it has
+ * zero val_len, then:
+ * . dm_hash_lookup() returns it
+ * . dm_hash_lookup_with_val(val_len=0) returns it
+ * . dm_hash_remove() removes it
+ * . dm_hash_remove_with_val(val_len=0) removes it
+ *
+ * dm_hash_lookup_with_count() is a single call that will
+ * both lookup a key's value and check if there is more
+ * than one entry with the given key.
+ *
+ * (It is not meant to retrieve all the entries with the
+ * given key. In the common case where a single entry exists
+ * for the key, it is useful to have a single call that will
+ * both look up the value and indicate if multiple values
+ * exist for the key.)
+ *
+ * dm_hash_lookup_with_count:
+ * . If no entries exist, the function returns NULL, and
+ * the count is set to 0.
+ * . If only one entry exists, the value of that entry is
+ * returned and count is set to 1.
+ * . If N entries exists, the value of the first entry is
+ * returned and count is set to N.
+ */
+
+void *dm_hash_lookup_with_val(struct dm_hash_table *t, const char *key,
+ const void *val, uint32_t val_len);
+void dm_hash_remove_with_val(struct dm_hash_table *t, const char *key,
+ const void *val, uint32_t val_len);
+int dm_hash_insert_allow_multiple(struct dm_hash_table *t, const char *key,
+ const void *val, uint32_t val_len);
+void *dm_hash_lookup_with_count(struct dm_hash_table *t, const char *key, int *count);
+
+
+#define dm_hash_iterate(v, h) \
+ for (v = dm_hash_get_first((h)); v; \
+ v = dm_hash_get_next((h), v))
+
+/*********
+ * selinux
+ *********/
+
+/*
+ * Obtain SELinux security context assigned for the path and set this
+ * context for creating a new file system object. This security context
+ * is global and it is used until reset to default policy behaviour
+ * by calling 'dm_prepare_selinux_context(NULL, 0)'.
+ */
+int dm_prepare_selinux_context(const char *path, mode_t mode);
+/*
+ * Set SELinux context for existing file system object.
+ */
+int dm_set_selinux_context(const char *path, mode_t mode);
+
+/*********************
+ * string manipulation
+ *********************/
+
+/*
+ * Break up the name of a mapped device into its constituent
+ * Volume Group, Logical Volume and Layer (if present).
+ * If mem is supplied, the result is allocated from the mempool.
+ * Otherwise the strings are changed in situ.
+ */
+int dm_split_lvm_name(struct dm_pool *mem, const char *dmname,
+ char **vgname, char **lvname, char **layer);
+
+/*
+ * Destructively split buffer into NULL-separated words in argv.
+ * Returns number of words.
+ */
+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, ...)
+ __attribute__ ((format(printf, 3, 4)));
+
+/*
+ * Returns pointer to the last component of the path.
+ */
+const char *dm_basename(const char *path);
+
+/*
+ * Returns number of occurrences of 'c' in 'str' of length 'size'.
+ */
+unsigned dm_count_chars(const char *str, size_t len, const int c);
+
+/*
+ * Length of string after escaping double quotes and backslashes.
+ */
+size_t dm_escaped_len(const char *str);
+
+/*
+ * <vg>-<lv>-<layer> or if !layer just <vg>-<lv>.
+ */
+char *dm_build_dm_name(struct dm_pool *mem, const char *vgname,
+ const char *lvname, const char *layer);
+char *dm_build_dm_uuid(struct dm_pool *mem, const char *prefix, const char *lvid, const char *layer);
+
+/*
+ * Copies a string, quoting double quotes with backslashes.
+ */
+char *dm_escape_double_quotes(char *out, const char *src);
+
+/*
+ * Undo quoting in situ.
+ */
+void dm_unescape_double_quotes(char *src);
+
+/*
+ * Unescape colons and "at" signs in situ and save the substrings
+ * starting at the position of the first unescaped colon and the
+ * first unescaped "at" sign. This is normally used to unescape
+ * device names used as PVs.
+ */
+void dm_unescape_colons_and_at_signs(char *src,
+ char **substr_first_unquoted_colon,
+ char **substr_first_unquoted_at_sign);
+
+/*
+ * Replacement for strncpy() function.
+ *
+ * Copies no more than n bytes from string pointed by src to the buffer
+ * pointed by dest and ensure string is finished with '\0'.
+ * Returns 0 if the whole string does not fit.
+ */
+int dm_strncpy(char *dest, const char *src, size_t n);
+
+/*
+ * Recognize unit specifier in the 'units' arg and return a factor
+ * representing that unit. If the 'units' contains a prefix with digits,
+ * the 'units' is considered to be a custom unit.
+ *
+ * Also, set 'unit_type' output arg to the character that represents
+ * the unit specified. The 'unit_type' character equals to the unit
+ * character itself recognized in the 'units' arg for canonical units.
+ * Otherwise, the 'unit_type' character is set to 'U' for custom unit.
+ *
+ * An example for k/K canonical units and 8k/8K custom units:
+ *
+ * units unit_type return value (factor)
+ * k k 1024
+ * K K 1000
+ * 8k U 1024*8
+ * 8K U 1000*8
+ * etc...
+ *
+ * Recognized units:
+ *
+ * h/H - human readable (returns 1 for both)
+ * b/B - byte (returns 1 for both)
+ * s/S - sector (returns 512 for both)
+ * k/K - kilo (returns 1024/1000 respectively)
+ * m/M - mega (returns 1024^2/1000^2 respectively)
+ * g/G - giga (returns 1024^3/1000^3 respectively)
+ * t/T - tera (returns 1024^4/1000^4 respectively)
+ * p/P - peta (returns 1024^5/1000^5 respectively)
+ * e/E - exa (returns 1024^6/1000^6 respectively)
+ *
+ * Only one units character is allowed in the 'units' arg
+ * if strict mode is enabled by 'strict' arg.
+ *
+ * The 'endptr' output arg, if not NULL, saves the pointer
+ * in the 'units' string which follows the unit specifier
+ * recognized (IOW the position where the parsing of the
+ * unit specifier stopped).
+ *
+ * Returns the unit factor or 0 if no unit is recognized.
+ */
+uint64_t dm_units_to_factor(const char *units, char *unit_type,
+ int strict, const char **endptr);
+
+/*
+ * Type of unit specifier used by dm_size_to_string().
+ */
+typedef enum {
+ DM_SIZE_LONG = 0, /* Megabyte */
+ DM_SIZE_SHORT = 1, /* MB or MiB */
+ DM_SIZE_UNIT = 2 /* M or m */
+} dm_size_suffix_t;
+
+/*
+ * Convert a size (in 512-byte sectors) into a printable string using units of unit_type.
+ * An upper-case unit_type indicates output units based on powers of 1000 are
+ * required; a lower-case unit_type indicates powers of 1024.
+ * For correct operation, unit_factor must be one of:
+ * 0 - the correct value will be calculated internally;
+ * or the output from dm_units_to_factor() corresponding to unit_type;
+ * or 'u' or 'U', an arbitrary number of bytes to use as the power base.
+ * Set include_suffix to 1 to include a suffix of suffix_type.
+ * Set use_si_units to 0 for suffixes that don't distinguish between 1000 and 1024.
+ * 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,
+ dm_size_suffix_t suffix_type);
+
+/**************************
+ * file/stream manipulation
+ **************************/
+
+/*
+ * Create a directory (with parent directories if necessary).
+ * Returns 1 on success, 0 on failure.
+ */
+int dm_create_dir(const char *dir);
+
+int dm_is_empty_dir(const char *dir);
+
+/*
+ * Close a stream, with nicer error checking than fclose's.
+ * Derived from gnulib's close-stream.c.
+ *
+ * Close "stream". Return 0 if successful, and EOF (setting errno)
+ * otherwise. Upon failure, set errno to 0 if the error number
+ * cannot be determined. Useful mainly for writable streams.
+ */
+int dm_fclose(FILE *stream);
+
+/*
+ * Returns size of a buffer which is allocated with dm_malloc.
+ * Pointer to the buffer is stored in *buf.
+ * Returns -1 on failure leaving buf undefined.
+ */
+int dm_asprintf(char **buf, const char *format, ...)
+ __attribute__ ((format(printf, 2, 3)));
+int dm_vasprintf(char **buf, const char *format, va_list ap)
+ __attribute__ ((format(printf, 2, 0)));
+
+/*
+ * create lockfile (pidfile) - create and lock a lock file
+ * @lockfile: location of lock file
+ *
+ * Returns: 1 on success, 0 otherwise, errno is handled internally
+ */
+int dm_create_lockfile(const char* lockfile);
+
+/*
+ * Query whether a daemon is running based on its lockfile
+ *
+ * Returns: 1 if running, 0 if not
+ */
+int dm_daemon_is_running(const char* lockfile);
+
+/*********************
+ * regular expressions
+ *********************/
+struct dm_regex;
+
+/*
+ * Initialise an array of num patterns for matching.
+ * Uses memory from mem.
+ */
+struct dm_regex *dm_regex_create(struct dm_pool *mem, const char * const *patterns,
+ unsigned num_patterns);
+
+/*
+ * Match string s against the patterns.
+ * Returns the index of the highest pattern in the array that matches,
+ * or -1 if none match.
+ */
+int dm_regex_match(struct dm_regex *regex, const char *s);
+
+/*
+ * This is useful for regression testing only. The idea is if two
+ * fingerprints are different, then the two dfas are certainly not
+ * isomorphic. If two fingerprints _are_ the same then it's very likely
+ * that the dfas are isomorphic.
+ *
+ * This function must be called before any matching is done.
+ */
+uint32_t dm_regex_fingerprint(struct dm_regex *regex);
+
+/******************
+ * percent handling
+ ******************/
+/*
+ * A fixed-point representation of percent values. One percent equals to
+ * DM_PERCENT_1 as defined below. Values that are not multiples of DM_PERCENT_1
+ * represent fractions, with precision of 1/1000000 of a percent. See
+ * dm_percent_to_float for a conversion to a floating-point representation.
+ *
+ * You should always use dm_make_percent when building dm_percent_t values. The
+ * implementation of dm_make_percent is biased towards the middle: it ensures that
+ * the result is DM_PERCENT_0 or DM_PERCENT_100 if and only if this is the actual
+ * value -- it never rounds any intermediate value (> 0 or < 100) to either 0
+ * or 100.
+*/
+#define DM_PERCENT_CHAR '%'
+
+typedef enum {
+ DM_PERCENT_0 = 0,
+ DM_PERCENT_1 = 1000000,
+ DM_PERCENT_100 = 100 * DM_PERCENT_1,
+ DM_PERCENT_INVALID = -1,
+ DM_PERCENT_FAILED = -2
+} dm_percent_range_t;
+
+typedef int32_t dm_percent_t;
+
+float dm_percent_to_float(dm_percent_t percent);
+/*
+ * Return adjusted/rounded float for better percent value printing.
+ * Function ensures for given precision of digits:
+ * 100.0% returns only when the value is DM_PERCENT_100
+ * for close smaller values rounds to nearest smaller value
+ * 0.0% returns only for value DM_PERCENT_0
+ * for close bigger values rounds to nearest bigger value
+ * In all other cases returns same value as dm_percent_to_float()
+ */
+float dm_percent_to_round_float(dm_percent_t percent, unsigned digits);
+dm_percent_t dm_make_percent(uint64_t numerator, uint64_t denominator);
+
+/********************
+ * timestamp handling
+ ********************/
+
+/*
+ * Create a dm_timestamp object to use with dm_timestamp_get.
+ */
+struct dm_timestamp *dm_timestamp_alloc(void);
+
+/*
+ * Update dm_timestamp object to represent the current time.
+ */
+int dm_timestamp_get(struct dm_timestamp *ts);
+
+/*
+ * Copy a timestamp from ts_old to ts_new.
+ */
+void dm_timestamp_copy(struct dm_timestamp *ts_new, struct dm_timestamp *ts_old);
+
+/*
+ * 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
+ */
+int dm_timestamp_compare(struct dm_timestamp *ts1, struct dm_timestamp *ts2);
+
+/*
+ * Return the absolute difference in nanoseconds between
+ * the dm_timestamp objects ts1 and ts2.
+ *
+ * Callers that need to know whether ts1 is before, equal to, or after ts2
+ * in addition to the magnitude should use dm_timestamp_compare.
+ */
+uint64_t dm_timestamp_delta(struct dm_timestamp *ts1, struct dm_timestamp *ts2);
+
+/*
+ * Destroy a dm_timestamp object.
+ */
+void dm_timestamp_destroy(struct dm_timestamp *ts);
+
+/*********************
+ * reporting functions
+ *********************/
+
+struct dm_report_object_type {
+ uint32_t id; /* Powers of 2 */
+ const char *desc;
+ const char *prefix; /* field id string prefix (optional) */
+ /* FIXME: convert to proper usage of const pointers here */
+ void *(*data_fn)(void *object); /* callback from report_object() */
+};
+
+struct dm_report_field;
+
+/*
+ * dm_report_field_type flags
+ */
+#define DM_REPORT_FIELD_MASK 0x00000FFF
+#define DM_REPORT_FIELD_ALIGN_MASK 0x0000000F
+#define DM_REPORT_FIELD_ALIGN_LEFT 0x00000001
+#define DM_REPORT_FIELD_ALIGN_RIGHT 0x00000002
+#define DM_REPORT_FIELD_TYPE_MASK 0x00000FF0
+#define DM_REPORT_FIELD_TYPE_NONE 0x00000000
+#define DM_REPORT_FIELD_TYPE_STRING 0x00000010
+#define DM_REPORT_FIELD_TYPE_NUMBER 0x00000020
+#define DM_REPORT_FIELD_TYPE_SIZE 0x00000040
+#define DM_REPORT_FIELD_TYPE_PERCENT 0x00000080
+#define DM_REPORT_FIELD_TYPE_STRING_LIST 0x00000100
+#define DM_REPORT_FIELD_TYPE_TIME 0x00000200
+
+/* For use with reserved values only! */
+#define DM_REPORT_FIELD_RESERVED_VALUE_MASK 0x0000000F
+#define DM_REPORT_FIELD_RESERVED_VALUE_NAMED 0x00000001 /* only named value, less strict form of reservation */
+#define DM_REPORT_FIELD_RESERVED_VALUE_RANGE 0x00000002 /* value is range - low and high value defined */
+#define DM_REPORT_FIELD_RESERVED_VALUE_DYNAMIC_VALUE 0x00000004 /* value is computed in runtime */
+#define DM_REPORT_FIELD_RESERVED_VALUE_FUZZY_NAMES 0x00000008 /* value names are recognized in runtime */
+
+#define DM_REPORT_FIELD_TYPE_ID_LEN 32
+#define DM_REPORT_FIELD_TYPE_HEADING_LEN 32
+
+struct dm_report;
+struct dm_report_field_type {
+ uint32_t type; /* object type id */
+ uint32_t flags; /* DM_REPORT_FIELD_* */
+ uint32_t offset; /* byte offset in the object */
+ int32_t width; /* default width */
+ /* string used to specify the field */
+ const char id[DM_REPORT_FIELD_TYPE_ID_LEN];
+ /* string printed in header */
+ 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,
+ void *private_data);
+ const char *desc; /* description of the field */
+};
+
+/*
+ * Per-field reserved value.
+ */
+struct dm_report_field_reserved_value {
+ /* field_num is the position of the field in 'fields'
+ array passed to dm_report_init_with_selection */
+ uint32_t field_num;
+ /* the value is of the same type as the field
+ identified by field_num */
+ const void *value;
+};
+
+/*
+ * Reserved value is a 'value' that is used directly if any of the 'names' is hit
+ * or in case of fuzzy names, if such fuzzy name matches.
+ *
+ * If type is any of DM_REPORT_FIELD_TYPE_*, the reserved value is recognized
+ * for all fields of that type.
+ *
+ * If type is DM_REPORT_FIELD_TYPE_NONE, the reserved value is recognized
+ * for the exact field specified - hence the type of the value is automatically
+ * the same as the type of the field itself.
+ *
+ * The array of reserved values is used to initialize reporting with
+ * selection enabled (see also dm_report_init_with_selection function).
+ */
+struct dm_report_reserved_value {
+ const uint32_t type; /* DM_REPORT_FIELD_RESERVED_VALUE_* and DM_REPORT_FIELD_TYPE_* */
+ const void *value; /* reserved value:
+ uint64_t for DM_REPORT_FIELD_TYPE_NUMBER
+ uint64_t for DM_REPORT_FIELD_TYPE_SIZE (number of 512-byte sectors)
+ uint64_t for DM_REPORT_FIELD_TYPE_PERCENT
+ const char* for DM_REPORT_FIELD_TYPE_STRING
+ struct dm_report_field_reserved_value for DM_REPORT_FIELD_TYPE_NONE
+ dm_report_reserved_handler* if DM_REPORT_FIELD_RESERVED_VALUE_{DYNAMIC_VALUE,FUZZY_NAMES} is used */
+ const char **names; /* null-terminated array of static names for this reserved value */
+ const char *description; /* description of the reserved value */
+};
+
+/*
+ * Available actions for dm_report_reserved_value_handler.
+ */
+typedef enum {
+ DM_REPORT_RESERVED_PARSE_FUZZY_NAME,
+ DM_REPORT_RESERVED_GET_DYNAMIC_VALUE,
+} dm_report_reserved_action_t;
+
+/*
+ * Generic reserved value handler to process reserved value names and/or values.
+ *
+ * Actions and their input/output:
+ *
+ * DM_REPORT_RESERVED_PARSE_FUZZY_NAME
+ * data_in: const char *fuzzy_name
+ * data_out: const char *canonical_name, NULL if fuzzy_name not recognized
+ *
+ * DM_REPORT_RESERVED_GET_DYNAMIC_VALUE
+ * data_in: const char *canonical_name
+ * data_out: void *value, NULL if canonical_name not recognized
+ *
+ * All actions return:
+ *
+ * -1 if action not implemented
+ * 0 on error
+ * 1 on success
+ */
+typedef int (*dm_report_reserved_handler) (struct dm_report *rh,
+ struct dm_pool *mem,
+ uint32_t field_num,
+ dm_report_reserved_action_t action,
+ const void *data_in,
+ const void **data_out);
+
+/*
+ * 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
+ * selection processing (dm_report_reserved_handler instances) to avoid
+ * recalculation of these values or to share values among calls.
+ */
+int dm_report_value_cache_set(struct dm_report *rh, const char *name, const void *data);
+const void *dm_report_value_cache_get(struct dm_report *rh, const char *name);
+/*
+ * dm_report_init output_flags
+ */
+#define DM_REPORT_OUTPUT_MASK 0x000000FF
+#define DM_REPORT_OUTPUT_ALIGNED 0x00000001
+#define DM_REPORT_OUTPUT_BUFFERED 0x00000002
+#define DM_REPORT_OUTPUT_HEADINGS 0x00000004
+#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_MULTIPLE_TIMES 0x00000040
+
+struct dm_report *dm_report_init(uint32_t *report_types,
+ const struct dm_report_object_type *types,
+ const struct dm_report_field_type *fields,
+ const char *output_fields,
+ const char *output_separator,
+ uint32_t output_flags,
+ const char *sort_keys,
+ void *private_data);
+struct dm_report *dm_report_init_with_selection(uint32_t *report_types,
+ const struct dm_report_object_type *types,
+ const struct dm_report_field_type *fields,
+ const char *output_fields,
+ const char *output_separator,
+ uint32_t output_flags,
+ const char *sort_keys,
+ const char *selection,
+ const struct dm_report_reserved_value reserved_values[],
+ void *private_data);
+/*
+ * Report an object, pass it through the selection criteria if they
+ * are present and display the result on output if it passes the criteria.
+ */
+int dm_report_object(struct dm_report *rh, void *object);
+/*
+ * The same as dm_report_object, but display the result on output only if
+ * 'do_output' arg is set. Also, save the result of selection in 'selected'
+ * arg if it's not NULL (either 1 if the object passes, otherwise 0).
+ */
+int dm_report_object_is_selected(struct dm_report *rh, void *object, int do_output, int *selected);
+
+/*
+ * Compact report output so that if field value is empty for all rows in
+ * the report, drop the field from output completely (including headers).
+ * Compact output is applicable only if report is buffered, otherwise
+ * this function has no effect.
+ */
+int dm_report_compact_fields(struct dm_report *rh);
+
+/*
+ * The same as dm_report_compact_fields, but for selected fields only.
+ * The "fields" arg is comma separated list of field names (the same format
+ * as used for "output_fields" arg in dm_report_init fn).
+ */
+int dm_report_compact_given_fields(struct dm_report *rh, const char *fields);
+
+/*
+ * Returns 1 if there is no data waiting to be output.
+ */
+int dm_report_is_empty(struct dm_report *rh);
+
+/*
+ * Destroy report content without doing output.
+ */
+void dm_report_destroy_rows(struct dm_report *rh);
+
+int dm_report_output(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.
+ */
+int dm_report_column_headings(struct dm_report *rh);
+
+void dm_report_free(struct dm_report *rh);
+
+/*
+ * Prefix added to each field name with DM_REPORT_OUTPUT_FIELD_NAME_PREFIX
+ */
+int dm_report_set_output_field_name_prefix(struct dm_report *rh,
+ const char *report_prefix);
+
+int dm_report_set_selection(struct dm_report *rh, const char *selection);
+
+/*
+ * Report functions are provided for simple data types.
+ * They take care of allocating copies of the data.
+ */
+int dm_report_field_string(struct dm_report *rh, struct dm_report_field *field,
+ const char *const *data);
+int dm_report_field_string_list(struct dm_report *rh, struct dm_report_field *field,
+ const struct dm_list *data, const char *delimiter);
+int dm_report_field_string_list_unsorted(struct dm_report *rh, struct dm_report_field *field,
+ const struct dm_list *data, const char *delimiter);
+int dm_report_field_int32(struct dm_report *rh, struct dm_report_field *field,
+ const int32_t *data);
+int dm_report_field_uint32(struct dm_report *rh, struct dm_report_field *field,
+ const uint32_t *data);
+int dm_report_field_int(struct dm_report *rh, struct dm_report_field *field,
+ const int *data);
+int dm_report_field_uint64(struct dm_report *rh, struct dm_report_field *field,
+ const uint64_t *data);
+int dm_report_field_percent(struct dm_report *rh, struct dm_report_field *field,
+ const dm_percent_t *data);
+
+/*
+ * For custom fields, allocate the data in 'mem' and use
+ * dm_report_field_set_value().
+ * 'sortvalue' may be NULL if it matches 'value'
+ */
+void dm_report_field_set_value(struct dm_report_field *field, const void *value,
+ const void *sortvalue);
+
+/*
+ * Report group support.
+ */
+struct dm_report_group;
+
+typedef enum {
+ DM_REPORT_GROUP_SINGLE,
+ DM_REPORT_GROUP_BASIC,
+ DM_REPORT_GROUP_JSON
+} dm_report_group_type_t;
+
+struct dm_report_group *dm_report_group_create(dm_report_group_type_t type, void *data);
+int dm_report_group_push(struct dm_report_group *group, struct dm_report *report, void *data);
+int dm_report_group_pop(struct dm_report_group *group);
+int dm_report_group_output_and_pop_all(struct dm_report_group *group);
+int dm_report_group_destroy(struct dm_report_group *group);
+
+/*
+ * Stats counter access methods
+ *
+ * Each method returns the corresponding stats counter value from 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.
+ *
+ * Two methods are provided to access counter values: a named function
+ * for each available counter field and a single function that accepts
+ * an enum value specifying the required field. New code is encouraged
+ * to use the enum based interface as calls to the named functions are
+ * implemented using the enum method internally.
+ *
+ * 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
+#define DM_STATS_AREA_CURRENT UINT64_MAX
+
+typedef enum {
+ DM_STATS_READS_COUNT,
+ DM_STATS_READS_MERGED_COUNT,
+ DM_STATS_READ_SECTORS_COUNT,
+ DM_STATS_READ_NSECS,
+ DM_STATS_WRITES_COUNT,
+ DM_STATS_WRITES_MERGED_COUNT,
+ DM_STATS_WRITE_SECTORS_COUNT,
+ DM_STATS_WRITE_NSECS,
+ DM_STATS_IO_IN_PROGRESS_COUNT,
+ DM_STATS_IO_NSECS,
+ DM_STATS_WEIGHTED_IO_NSECS,
+ DM_STATS_TOTAL_READ_NSECS,
+ DM_STATS_TOTAL_WRITE_NSECS,
+ DM_STATS_NR_COUNTERS
+} dm_stats_counter_t;
+
+uint64_t dm_stats_get_counter(const struct dm_stats *dms,
+ dm_stats_counter_t counter,
+ uint64_t region_id, uint64_t area_id);
+
+uint64_t dm_stats_get_reads(const struct dm_stats *dms,
+ uint64_t region_id, uint64_t area_id);
+
+uint64_t dm_stats_get_reads_merged(const struct dm_stats *dms,
+ uint64_t region_id, uint64_t area_id);
+
+uint64_t dm_stats_get_read_sectors(const struct dm_stats *dms,
+ uint64_t region_id, uint64_t area_id);
+
+uint64_t dm_stats_get_read_nsecs(const struct dm_stats *dms,
+ uint64_t region_id, uint64_t area_id);
+
+uint64_t dm_stats_get_writes(const struct dm_stats *dms,
+ uint64_t region_id, uint64_t area_id);
+
+uint64_t dm_stats_get_writes_merged(const struct dm_stats *dms,
+ uint64_t region_id, uint64_t area_id);
+
+uint64_t dm_stats_get_write_sectors(const struct dm_stats *dms,
+ uint64_t region_id, uint64_t area_id);
+
+uint64_t dm_stats_get_write_nsecs(const struct dm_stats *dms,
+ uint64_t region_id, uint64_t area_id);
+
+uint64_t dm_stats_get_io_in_progress(const struct dm_stats *dms,
+ uint64_t region_id, uint64_t area_id);
+
+uint64_t dm_stats_get_io_nsecs(const struct dm_stats *dms,
+ uint64_t region_id, uint64_t area_id);
+
+uint64_t dm_stats_get_weighted_io_nsecs(const struct dm_stats *dms,
+ uint64_t region_id, uint64_t area_id);
+
+uint64_t dm_stats_get_total_read_nsecs(const struct dm_stats *dms,
+ uint64_t region_id, uint64_t area_id);
+
+uint64_t dm_stats_get_total_write_nsecs(const 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
+ */
+
+typedef enum {
+ DM_STATS_RD_MERGES_PER_SEC,
+ DM_STATS_WR_MERGES_PER_SEC,
+ DM_STATS_READS_PER_SEC,
+ DM_STATS_WRITES_PER_SEC,
+ DM_STATS_READ_SECTORS_PER_SEC,
+ DM_STATS_WRITE_SECTORS_PER_SEC,
+ DM_STATS_AVERAGE_REQUEST_SIZE,
+ DM_STATS_AVERAGE_QUEUE_SIZE,
+ DM_STATS_AVERAGE_WAIT_TIME,
+ DM_STATS_AVERAGE_RD_WAIT_TIME,
+ DM_STATS_AVERAGE_WR_WAIT_TIME,
+ DM_STATS_SERVICE_TIME,
+ DM_STATS_THROUGHPUT,
+ DM_STATS_UTILIZATION,
+ DM_STATS_NR_METRICS
+} dm_stats_metric_t;
+
+int dm_stats_get_metric(const struct dm_stats *dms, int metric,
+ uint64_t region_id, uint64_t area_id, double *value);
+
+int dm_stats_get_rd_merges_per_sec(const struct dm_stats *dms, double *rrqm,
+ uint64_t region_id, uint64_t area_id);
+
+int dm_stats_get_wr_merges_per_sec(const struct dm_stats *dms, double *rrqm,
+ uint64_t region_id, uint64_t area_id);
+
+int dm_stats_get_reads_per_sec(const struct dm_stats *dms, double *rd_s,
+ uint64_t region_id, uint64_t area_id);
+
+int dm_stats_get_writes_per_sec(const struct dm_stats *dms, double *wr_s,
+ uint64_t region_id, uint64_t area_id);
+
+int dm_stats_get_read_sectors_per_sec(const struct dm_stats *dms,
+ double *rsec_s, uint64_t region_id,
+ uint64_t area_id);
+
+int dm_stats_get_write_sectors_per_sec(const struct dm_stats *dms,
+ double *wr_s, uint64_t region_id,
+ uint64_t area_id);
+
+int dm_stats_get_average_request_size(const struct dm_stats *dms,
+ double *arqsz, uint64_t region_id,
+ uint64_t area_id);
+
+int dm_stats_get_service_time(const struct dm_stats *dms, double *svctm,
+ uint64_t region_id, uint64_t area_id);
+
+int dm_stats_get_average_queue_size(const struct dm_stats *dms, double *qusz,
+ uint64_t region_id, uint64_t area_id);
+
+int dm_stats_get_average_wait_time(const struct dm_stats *dms, double *await,
+ uint64_t region_id, uint64_t area_id);
+
+int dm_stats_get_average_rd_wait_time(const struct dm_stats *dms,
+ double *await, uint64_t region_id,
+ uint64_t area_id);
+
+int dm_stats_get_average_wr_wait_time(const struct dm_stats *dms,
+ double *await, uint64_t region_id,
+ uint64_t area_id);
+
+int dm_stats_get_throughput(const struct dm_stats *dms, double *tput,
+ uint64_t region_id, uint64_t area_id);
+
+int dm_stats_get_utilization(const struct dm_stats *dms, dm_percent_t *util,
+ uint64_t region_id, uint64_t area_id);
+
+/*
+ * Statistics histogram access methods.
+ *
+ * Methods to access latency histograms for regions that have them
+ * enabled. Each histogram contains a configurable number of bins
+ * spanning a user defined latency interval.
+ *
+ * The bin count, upper and lower bin bounds, and bin values are
+ * made available via the following area methods.
+ *
+ * Methods to obtain a simple string representation of the histogram
+ * and its bounds are also provided.
+ */
+
+/*
+ * Retrieve a pointer to the histogram associated with the specified
+ * area. If the area does not have a histogram configured this function
+ * returns NULL.
+ *
+ * The pointer does not need to be freed explicitly by the caller: it
+ * will become invalid following a subsequent dm_stats_list(),
+ * dm_stats_populate() or dm_stats_destroy() of the corresponding
+ * dm_stats handle.
+ *
+ * If region_id or area_id is one of the special values
+ * DM_STATS_REGION_CURRENT or DM_STATS_AREA_CURRENT the current cursor
+ * value is used to select the region or area.
+ */
+struct dm_histogram *dm_stats_get_histogram(const struct dm_stats *dms,
+ uint64_t region_id,
+ uint64_t area_id);
+
+/*
+ * Return the number of bins in the specified histogram handle.
+ */
+int dm_histogram_get_nr_bins(const struct dm_histogram *dmh);
+
+/*
+ * Get the lower bound of the specified bin of the histogram for the
+ * area specified by region_id and area_id. The value is returned in
+ * nanoseconds.
+ */
+uint64_t dm_histogram_get_bin_lower(const struct dm_histogram *dmh, int bin);
+
+/*
+ * Get the upper bound of the specified bin of the histogram for the
+ * area specified by region_id and area_id. The value is returned in
+ * nanoseconds.
+ */
+uint64_t dm_histogram_get_bin_upper(const struct dm_histogram *dmh, int bin);
+
+/*
+ * Get the width of the specified bin of the histogram for the area
+ * specified by region_id and area_id. The width is equal to the bin
+ * upper bound minus the lower bound and yields the range of latency
+ * values covered by this bin. The value is returned in nanoseconds.
+ */
+uint64_t dm_histogram_get_bin_width(const struct dm_histogram *dmh, int bin);
+
+/*
+ * Get the value of the specified bin of the histogram for the area
+ * specified by region_id and area_id.
+ */
+uint64_t dm_histogram_get_bin_count(const struct dm_histogram *dmh, int bin);
+
+/*
+ * Get the percentage (relative frequency) of the specified bin of the
+ * histogram for the area specified by region_id and area_id.
+ */
+dm_percent_t dm_histogram_get_bin_percent(const struct dm_histogram *dmh,
+ int bin);
+
+/*
+ * Return the total observations (sum of bin counts) for the histogram
+ * of the area specified by region_id and area_id.
+ */
+uint64_t dm_histogram_get_sum(const struct dm_histogram *dmh);
+
+/*
+ * Histogram formatting flags.
+ */
+#define DM_HISTOGRAM_SUFFIX 0x1
+#define DM_HISTOGRAM_VALUES 0x2
+#define DM_HISTOGRAM_PERCENT 0X4
+#define DM_HISTOGRAM_BOUNDS_LOWER 0x10
+#define DM_HISTOGRAM_BOUNDS_UPPER 0x20
+#define DM_HISTOGRAM_BOUNDS_RANGE 0x30
+
+/*
+ * Return a string representation of the supplied histogram's values and
+ * bin boundaries.
+ *
+ * The bin argument selects the bin to format. If this argument is less
+ * than zero all bins will be included in the resulting string.
+ *
+ * width specifies a minimum width for the field in characters; if it is
+ * zero the width will be determined automatically based on the options
+ * selected for formatting. A value less than zero disables field width
+ * control: bin boundaries and values will be output with a minimum
+ * amount of whitespace.
+ *
+ * flags is a collection of flag arguments that control the string format:
+ *
+ * DM_HISTOGRAM_VALUES - Include bin values in the string.
+ * DM_HISTOGRAM_SUFFIX - Include time unit suffixes when printing bounds.
+ * DM_HISTOGRAM_PERCENT - Format bin values as a percentage.
+ *
+ * DM_HISTOGRAM_BOUNDS_LOWER - Include the lower bound of each bin.
+ * DM_HISTOGRAM_BOUNDS_UPPER - Include the upper bound of each bin.
+ * DM_HISTOGRAM_BOUNDS_RANGE - Show the span of each bin as "lo-up".
+ *
+ * The returned pointer does not need to be freed explicitly by the
+ * caller: it will become invalid following a subsequent
+ * dm_stats_list(), dm_stats_populate() or dm_stats_destroy() of the
+ * corresponding dm_stats handle.
+ */
+const char *dm_histogram_to_string(const struct dm_histogram *dmh, int bin,
+ int width, int flags);
+
+/*************************
+ * config file parse/print
+ *************************/
+typedef enum {
+ DM_CFG_INT,
+ DM_CFG_FLOAT,
+ DM_CFG_STRING,
+ DM_CFG_EMPTY_ARRAY
+} dm_config_value_type_t;
+
+struct dm_config_value {
+ dm_config_value_type_t type;
+
+ union {
+ int64_t i;
+ float f;
+ double d; /* Unused. */
+ const char *str;
+ } v;
+
+ struct dm_config_value *next; /* For arrays */
+ uint32_t format_flags;
+};
+
+struct dm_config_node {
+ const char *key;
+ struct dm_config_node *parent, *sib, *child;
+ struct dm_config_value *v;
+ int id;
+};
+
+struct dm_config_tree {
+ struct dm_config_node *root;
+ struct dm_config_tree *cascade;
+ struct dm_pool *mem;
+ void *custom;
+};
+
+struct dm_config_tree *dm_config_create(void);
+struct dm_config_tree *dm_config_from_string(const char *config_settings);
+int dm_config_parse(struct dm_config_tree *cft, const char *start, const char *end);
+int dm_config_parse_without_dup_node_check(struct dm_config_tree *cft, const char *start, const char *end);
+
+void *dm_config_get_custom(struct dm_config_tree *cft);
+void dm_config_set_custom(struct dm_config_tree *cft, void *custom);
+
+/*
+ * When searching, first_cft is checked before second_cft.
+ */
+struct dm_config_tree *dm_config_insert_cascaded_tree(struct dm_config_tree *first_cft, struct dm_config_tree *second_cft);
+
+/*
+ * If there's a cascaded dm_config_tree, remove the top layer
+ * and return the layer below. Otherwise return NULL.
+ */
+struct dm_config_tree *dm_config_remove_cascaded_tree(struct dm_config_tree *cft);
+
+/*
+ * Create a new, uncascaded config tree equivalent to the input cascade.
+ */
+struct dm_config_tree *dm_config_flatten(struct dm_config_tree *cft);
+
+void dm_config_destroy(struct dm_config_tree *cft);
+
+/* Simple output line by line. */
+typedef int (*dm_putline_fn)(const char *line, void *baton);
+/* More advaced output with config node reference. */
+typedef int (*dm_config_node_out_fn)(const struct dm_config_node *cn, const char *line, void *baton);
+
+/*
+ * Specification for advanced config node output.
+ */
+struct dm_config_node_out_spec {
+ dm_config_node_out_fn prefix_fn; /* called before processing config node lines */
+ dm_config_node_out_fn line_fn; /* called for each config node line */
+ dm_config_node_out_fn suffix_fn; /* called after processing config node lines */
+};
+
+/* Write the node and any subsequent siblings it has. */
+int dm_config_write_node(const struct dm_config_node *cn, dm_putline_fn putline, void *baton);
+int dm_config_write_node_out(const struct dm_config_node *cn, const struct dm_config_node_out_spec *out_spec, void *baton);
+
+/* Write given node only without subsequent siblings. */
+int dm_config_write_one_node(const struct dm_config_node *cn, dm_putline_fn putline, void *baton);
+int dm_config_write_one_node_out(const struct dm_config_node *cn, const struct dm_config_node_out_spec *out_spec, void *baton);
+
+struct dm_config_node *dm_config_find_node(const struct dm_config_node *cn, const char *path);
+int dm_config_has_node(const struct dm_config_node *cn, const char *path);
+int dm_config_remove_node(struct dm_config_node *parent, struct dm_config_node *remove);
+const char *dm_config_find_str(const struct dm_config_node *cn, const char *path, const char *fail);
+const char *dm_config_find_str_allow_empty(const struct dm_config_node *cn, const char *path, const char *fail);
+int dm_config_find_int(const struct dm_config_node *cn, const char *path, int fail);
+int64_t dm_config_find_int64(const struct dm_config_node *cn, const char *path, int64_t fail);
+float dm_config_find_float(const struct dm_config_node *cn, const char *path, float fail);
+
+const struct dm_config_node *dm_config_tree_find_node(const struct dm_config_tree *cft, const char *path);
+const char *dm_config_tree_find_str(const struct dm_config_tree *cft, const char *path, const char *fail);
+const char *dm_config_tree_find_str_allow_empty(const struct dm_config_tree *cft, const char *path, const char *fail);
+int dm_config_tree_find_int(const struct dm_config_tree *cft, const char *path, int fail);
+int64_t dm_config_tree_find_int64(const struct dm_config_tree *cft, const char *path, int64_t fail);
+float dm_config_tree_find_float(const struct dm_config_tree *cft, const char *path, float fail);
+int dm_config_tree_find_bool(const struct dm_config_tree *cft, const char *path, int fail);
+
+/*
+ * Understands (0, ~0), (y, n), (yes, no), (on,
+ * off), (true, false).
+ */
+int dm_config_find_bool(const struct dm_config_node *cn, const char *path, int fail);
+int dm_config_value_is_bool(const struct dm_config_value *v);
+
+int dm_config_get_uint32(const struct dm_config_node *cn, const char *path, uint32_t *result);
+int dm_config_get_uint64(const struct dm_config_node *cn, const char *path, uint64_t *result);
+int dm_config_get_str(const struct dm_config_node *cn, const char *path, const char **result);
+int dm_config_get_list(const struct dm_config_node *cn, const char *path, const struct dm_config_value **result);
+int dm_config_get_section(const struct dm_config_node *cn, const char *path, const struct dm_config_node **result);
+
+unsigned dm_config_maybe_section(const char *str, unsigned len);
+
+const char *dm_config_parent_name(const struct dm_config_node *n);
+
+struct dm_config_node *dm_config_clone_node_with_mem(struct dm_pool *mem, const struct dm_config_node *node, int siblings);
+struct dm_config_node *dm_config_create_node(struct dm_config_tree *cft, const char *key);
+struct dm_config_value *dm_config_create_value(struct dm_config_tree *cft);
+struct dm_config_node *dm_config_clone_node(struct dm_config_tree *cft, const struct dm_config_node *cn, int siblings);
+
+/*
+ * Common formatting flags applicable to all config node types (lower 16 bits).
+ */
+#define DM_CONFIG_VALUE_FMT_COMMON_ARRAY 0x00000001 /* value is array */
+#define DM_CONFIG_VALUE_FMT_COMMON_EXTRA_SPACES 0x00000002 /* add spaces in "key = value" pairs in constrast to "key=value" for better readability */
+
+/*
+ * Type-related config node formatting flags (higher 16 bits).
+ */
+/* int-related formatting flags */
+#define DM_CONFIG_VALUE_FMT_INT_OCTAL 0x00010000 /* print number in octal form */
+
+/* string-related formatting flags */
+#define DM_CONFIG_VALUE_FMT_STRING_NO_QUOTES 0x00010000 /* do not print quotes around string value */
+
+void dm_config_value_set_format_flags(struct dm_config_value *cv, uint32_t format_flags);
+uint32_t dm_config_value_get_format_flags(struct dm_config_value *cv);
+
+struct dm_pool *dm_config_memory(struct dm_config_tree *cft);
+
+/* Udev device directory. */
+#define DM_UDEV_DEV_DIR "/dev/"
+
+/* Cookie prefixes.
+ *
+ * The cookie value consists of a prefix (16 bits) and a base (16 bits).
+ * We can use the prefix to store the flags. These flags are sent to
+ * kernel within given dm task. When returned back to userspace in
+ * DM_COOKIE udev environment variable, we can control several aspects
+ * of udev rules we use by decoding the cookie prefix. When doing the
+ * notification, we replace the cookie prefix with DM_COOKIE_MAGIC,
+ * so we notify the right semaphore.
+ *
+ * It is still possible to use cookies for passing the flags to udev
+ * rules even when udev_sync is disabled. The base part of the cookie
+ * will be zero (there's no notification semaphore) and prefix will be
+ * set then. However, having udev_sync enabled is highly recommended.
+ */
+#define DM_COOKIE_MAGIC 0x0D4D
+#define DM_UDEV_FLAGS_MASK 0xFFFF0000
+#define DM_UDEV_FLAGS_SHIFT 16
+
+/*
+ * DM_UDEV_DISABLE_DM_RULES_FLAG is set in case we need to disable
+ * basic device-mapper udev rules that create symlinks in /dev/<DM_DIR>
+ * directory. However, we can't reliably prevent creating default
+ * nodes by udev (commonly /dev/dm-X, where X is a number).
+ */
+#define DM_UDEV_DISABLE_DM_RULES_FLAG 0x0001
+/*
+ * DM_UDEV_DISABLE_SUBSYTEM_RULES_FLAG is set in case we need to disable
+ * subsystem udev rules, but still we need the general DM udev rules to
+ * be applied (to create the nodes and symlinks under /dev and /dev/disk).
+ */
+#define DM_UDEV_DISABLE_SUBSYSTEM_RULES_FLAG 0x0002
+/*
+ * DM_UDEV_DISABLE_DISK_RULES_FLAG is set in case we need to disable
+ * general DM rules that set symlinks in /dev/disk directory.
+ */
+#define DM_UDEV_DISABLE_DISK_RULES_FLAG 0x0004
+/*
+ * DM_UDEV_DISABLE_OTHER_RULES_FLAG is set in case we need to disable
+ * all the other rules that are not general device-mapper nor subsystem
+ * related (the rules belong to other software or packages). All foreign
+ * rules should check this flag directly and they should ignore further
+ * rule processing for such event.
+ */
+#define DM_UDEV_DISABLE_OTHER_RULES_FLAG 0x0008
+/*
+ * DM_UDEV_LOW_PRIORITY_FLAG is set in case we need to instruct the
+ * udev rules to give low priority to the device that is currently
+ * processed. For example, this provides a way to select which symlinks
+ * could be overwritten by high priority ones if their names are equal.
+ * Common situation is a name based on FS UUID while using origin and
+ * snapshot devices.
+ */
+#define DM_UDEV_LOW_PRIORITY_FLAG 0x0010
+/*
+ * DM_UDEV_DISABLE_LIBRARY_FALLBACK is set in case we need to disable
+ * libdevmapper's node management. We will rely on udev completely
+ * and there will be no fallback action provided by libdevmapper if
+ * udev does something improperly. Using the library fallback code has
+ * a consequence that you need to take into account: any device node
+ * or symlink created without udev is not recorded in udev database
+ * which other applications may read to get complete list of devices.
+ * For this reason, use of DM_UDEV_DISABLE_LIBRARY_FALLBACK is
+ * recommended on systems where udev is used. Keep library fallback
+ * enabled just for exceptional cases where you need to debug udev-related
+ * problems. If you hit such problems, please contact us through upstream
+ * LVM2 development mailing list (see also README file). This flag is
+ * currently not set by default in libdevmapper so you need to set it
+ * explicitly if you're sure that udev is behaving correctly on your
+ * setups.
+ */
+#define DM_UDEV_DISABLE_LIBRARY_FALLBACK 0x0020
+/*
+ * DM_UDEV_PRIMARY_SOURCE_FLAG is automatically appended by
+ * libdevmapper for all ioctls generating udev uevents. Once used in
+ * udev rules, we know if this is a real "primary sourced" event or not.
+ * We need to distinguish real events originated in libdevmapper from
+ * any spurious events to gather all missing information (e.g. events
+ * generated as a result of "udevadm trigger" command or as a result
+ * of the "watch" udev rule).
+ */
+#define DM_UDEV_PRIMARY_SOURCE_FLAG 0x0040
+
+/*
+ * Udev flags reserved for use by any device-mapper subsystem.
+ */
+#define DM_SUBSYSTEM_UDEV_FLAG0 0x0100
+#define DM_SUBSYSTEM_UDEV_FLAG1 0x0200
+#define DM_SUBSYSTEM_UDEV_FLAG2 0x0400
+#define DM_SUBSYSTEM_UDEV_FLAG3 0x0800
+#define DM_SUBSYSTEM_UDEV_FLAG4 0x1000
+#define DM_SUBSYSTEM_UDEV_FLAG5 0x2000
+#define DM_SUBSYSTEM_UDEV_FLAG6 0x4000
+#define DM_SUBSYSTEM_UDEV_FLAG7 0x8000
+
+int dm_cookie_supported(void);
+
+/*
+ * Udev synchronisation functions.
+ */
+void dm_udev_set_sync_support(int sync_with_udev);
+int dm_udev_get_sync_support(void);
+void dm_udev_set_checking(int checking);
+int dm_udev_get_checking(void);
+
+/*
+ * Default value to get new auto generated cookie created
+ */
+#define DM_COOKIE_AUTO_CREATE 0
+int dm_udev_create_cookie(uint32_t *cookie);
+int dm_udev_complete(uint32_t cookie);
+int dm_udev_wait(uint32_t cookie);
+
+/*
+ * dm_dev_wait_immediate
+ * If *ready is 1 on return, the wait is complete.
+ * If *ready is 0 on return, the wait is incomplete and either
+ * this function or dm_udev_wait() must be called again.
+ * Returns 0 on error, when neither function should be called again.
+ */
+int dm_udev_wait_immediate(uint32_t cookie, int *ready);
+
+#define DM_DEV_DIR_UMASK 0022
+#define DM_CONTROL_NODE_UMASK 0177
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* LIB_DEVICE_MAPPER_H */
diff --git a/device_mapper/libdevmapper.h b/device_mapper/libdevmapper.h
deleted file mode 100644
index f7ff4ce..0000000
--- a/device_mapper/libdevmapper.h
+++ /dev/null
@@ -1,3554 +0,0 @@
-/*
- * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
- * Copyright (C) 2004-2017 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2006 Rackable Systems All rights reserved.
- *
- * This file is part of the device-mapper userspace tools.
- *
- * This copyrighted material is made available to anyone wishing to use,
- * modify, copy, or redistribute it subject to the terms and conditions
- * of the GNU Lesser General Public License v.2.1.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#ifndef LIB_DEVICE_MAPPER_H
-#define LIB_DEVICE_MAPPER_H
-
-#include <inttypes.h>
-#include <stdarg.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-
-#ifdef __linux__
-# include <linux/types.h>
-#endif
-
-#include <limits.h>
-#include <string.h>
-#include <stdlib.h>
-#include <stdio.h>
-
-#include "base/data-struct/list.h"
-
-#include "base/data-struct/list.h"
-
-#ifndef __GNUC__
-# define __typeof__ typeof
-#endif
-
-/* Macros to make string defines */
-#define DM_TO_STRING_EXP(A) #A
-#define DM_TO_STRING(A) DM_TO_STRING_EXP(A)
-
-#define DM_ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/*****************************************************************
- * The first section of this file provides direct access to the
- * individual device-mapper ioctls. Since it is quite laborious to
- * build the ioctl arguments for the device-mapper, people are
- * encouraged to use this library.
- ****************************************************************/
-
-/*
- * The library user may wish to register their own
- * logging function. By default errors go to stderr.
- * Use dm_log_with_errno_init(NULL) to restore the default log fn.
- * Error messages may have a non-zero errno.
- * Debug messages may have a non-zero class.
- * Aborts on internal error when env DM_ABORT_ON_INTERNAL_ERRORS is 1
- */
-
-typedef void (*dm_log_with_errno_fn) (int level, const char *file, int line,
- int dm_errno_or_class, const char *f, ...)
- __attribute__ ((format(printf, 5, 6)));
-
-void dm_log_with_errno_init(dm_log_with_errno_fn fn);
-void dm_log_init_verbose(int level);
-
-/*
- * Original version of this function.
- * dm_errno is set to 0.
- *
- * Deprecated: Use the _with_errno_ versions above instead.
- */
-typedef void (*dm_log_fn) (int level, const char *file, int line,
- const char *f, ...)
- __attribute__ ((format(printf, 4, 5)));
-
-void dm_log_init(dm_log_fn fn);
-/*
- * For backward-compatibility, indicate that dm_log_init() was used
- * to set a non-default value of dm_log().
- */
-int dm_log_is_non_default(void);
-
-/*
- * Number of devices currently in suspended state (via the library).
- */
-int dm_get_suspended_counter(void);
-
-enum {
- DM_DEVICE_CREATE,
- DM_DEVICE_RELOAD,
- DM_DEVICE_REMOVE,
- DM_DEVICE_REMOVE_ALL,
-
- DM_DEVICE_SUSPEND,
- DM_DEVICE_RESUME,
-
- DM_DEVICE_INFO,
- DM_DEVICE_DEPS,
- DM_DEVICE_RENAME,
-
- DM_DEVICE_VERSION,
-
- DM_DEVICE_STATUS,
- DM_DEVICE_TABLE,
- DM_DEVICE_WAITEVENT,
-
- DM_DEVICE_LIST,
-
- DM_DEVICE_CLEAR,
-
- DM_DEVICE_MKNODES,
-
- DM_DEVICE_LIST_VERSIONS,
-
- DM_DEVICE_TARGET_MSG,
-
- DM_DEVICE_SET_GEOMETRY
-};
-
-/*
- * You will need to build a struct dm_task for
- * each ioctl command you want to execute.
- */
-
-struct dm_pool;
-struct dm_task;
-struct dm_timestamp;
-
-struct dm_task *dm_task_create(int type);
-void dm_task_destroy(struct dm_task *dmt);
-
-int dm_task_set_name(struct dm_task *dmt, const char *name);
-int dm_task_set_uuid(struct dm_task *dmt, const char *uuid);
-
-/*
- * Retrieve attributes after an info.
- */
-struct dm_info {
- int exists;
- int suspended;
- int live_table;
- int inactive_table;
- int32_t open_count;
- uint32_t event_nr;
- uint32_t major;
- uint32_t minor; /* minor device number */
- int read_only; /* 0:read-write; 1:read-only */
-
- int32_t target_count;
-
- int deferred_remove;
- int internal_suspend;
-};
-
-struct dm_deps {
- uint32_t count;
- uint32_t filler;
- uint64_t device[0];
-};
-
-struct dm_names {
- uint64_t dev;
- uint32_t next; /* Offset to next struct from start of this struct */
- char name[0];
-};
-
-struct dm_versions {
- uint32_t next; /* Offset to next struct from start of this struct */
- uint32_t version[3];
-
- char name[0];
-};
-
-int dm_get_library_version(char *version, size_t size);
-int dm_task_get_driver_version(struct dm_task *dmt, char *version, size_t size);
-int dm_task_get_info(struct dm_task *dmt, struct dm_info *dmi);
-
-/*
- * This function returns dm device's UUID based on the value
- * of the mangling mode set during preceding dm_task_run call:
- * - unmangled UUID for DM_STRING_MANGLING_{AUTO, HEX},
- * - UUID without any changes for DM_STRING_MANGLING_NONE.
- *
- * To get mangled or unmangled form of the UUID directly, use
- * dm_task_get_uuid_mangled or dm_task_get_uuid_unmangled function.
- */
-const char *dm_task_get_uuid(const struct dm_task *dmt);
-
-struct dm_deps *dm_task_get_deps(struct dm_task *dmt);
-struct dm_versions *dm_task_get_versions(struct dm_task *dmt);
-const char *dm_task_get_message_response(struct dm_task *dmt);
-
-/*
- * These functions return device-mapper names based on the value
- * of the mangling mode set during preceding dm_task_run call:
- * - unmangled name for DM_STRING_MANGLING_{AUTO, HEX},
- * - name without any changes for DM_STRING_MANGLING_NONE.
- *
- * To get mangled or unmangled form of the name directly, use
- * dm_task_get_name_mangled or dm_task_get_name_unmangled function.
- */
-const char *dm_task_get_name(const struct dm_task *dmt);
-struct dm_names *dm_task_get_names(struct dm_task *dmt);
-
-int dm_task_set_ro(struct dm_task *dmt);
-int dm_task_set_newname(struct dm_task *dmt, const char *newname);
-int dm_task_set_newuuid(struct dm_task *dmt, const char *newuuid);
-int dm_task_set_minor(struct dm_task *dmt, int minor);
-int dm_task_set_major(struct dm_task *dmt, int major);
-int dm_task_set_major_minor(struct dm_task *dmt, int major, int minor, int allow_default_major_fallback);
-int dm_task_set_uid(struct dm_task *dmt, uid_t uid);
-int dm_task_set_gid(struct dm_task *dmt, gid_t gid);
-int dm_task_set_mode(struct dm_task *dmt, mode_t mode);
-/* See also description for DM_UDEV_DISABLE_LIBRARY_FALLBACK flag! */
-int dm_task_set_cookie(struct dm_task *dmt, uint32_t *cookie, uint16_t flags);
-int dm_task_set_event_nr(struct dm_task *dmt, uint32_t event_nr);
-int dm_task_set_geometry(struct dm_task *dmt, const char *cylinders, const char *heads, const char *sectors, const char *start);
-int dm_task_set_message(struct dm_task *dmt, const char *message);
-int dm_task_set_sector(struct dm_task *dmt, uint64_t sector);
-int dm_task_no_flush(struct dm_task *dmt);
-int dm_task_no_open_count(struct dm_task *dmt);
-int dm_task_skip_lockfs(struct dm_task *dmt);
-int dm_task_query_inactive_table(struct dm_task *dmt);
-int dm_task_suppress_identical_reload(struct dm_task *dmt);
-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.
- */
-int dm_task_enable_checks(struct dm_task *dmt);
-
-typedef enum {
- DM_ADD_NODE_ON_RESUME, /* add /dev/mapper node with dmsetup resume */
- DM_ADD_NODE_ON_CREATE /* add /dev/mapper node with dmsetup create */
-} dm_add_node_t;
-int dm_task_set_add_node(struct dm_task *dmt, dm_add_node_t add_node);
-
-/*
- * Control read_ahead.
- */
-#define DM_READ_AHEAD_AUTO UINT32_MAX /* Use kernel default readahead */
-#define DM_READ_AHEAD_NONE 0 /* Disable readahead */
-
-#define DM_READ_AHEAD_MINIMUM_FLAG 0x1 /* Value supplied is minimum */
-
-/*
- * Read ahead is set with DM_DEVICE_CREATE with a table or DM_DEVICE_RESUME.
- */
-int dm_task_set_read_ahead(struct dm_task *dmt, uint32_t read_ahead,
- uint32_t read_ahead_flags);
-uint32_t dm_task_get_read_ahead(const struct dm_task *dmt,
- uint32_t *read_ahead);
-
-/*
- * Use these to prepare for a create or reload.
- */
-int dm_task_add_target(struct dm_task *dmt,
- uint64_t start,
- uint64_t size, const char *ttype, const char *params);
-
-/*
- * Format major/minor numbers correctly for input to driver.
- */
-#define DM_FORMAT_DEV_BUFSIZE 13 /* Minimum bufsize to handle worst case. */
-int dm_format_dev(char *buf, int bufsize, uint32_t dev_major, uint32_t dev_minor);
-
-/* Use this to retrive target information returned from a STATUS call */
-void *dm_get_next_target(struct dm_task *dmt,
- void *next, uint64_t *start, uint64_t *length,
- char **target_type, char **params);
-
-/*
- * Following dm_get_status_* functions will allocate approriate status structure
- * from passed mempool together with the necessary character arrays.
- * Destroying the mempool will release all asociated allocation.
- */
-
-/* Parse params from STATUS call for mirror target */
-typedef enum {
- DM_STATUS_MIRROR_ALIVE = 'A',/* No failures */
- DM_STATUS_MIRROR_FLUSH_FAILED = 'F',/* Mirror out-of-sync */
- DM_STATUS_MIRROR_WRITE_FAILED = 'D',/* Mirror out-of-sync */
- DM_STATUS_MIRROR_SYNC_FAILED = 'S',/* Mirror out-of-sync */
- DM_STATUS_MIRROR_READ_FAILED = 'R',/* Mirror data unaffected */
- DM_STATUS_MIRROR_UNCLASSIFIED = 'U' /* Bug */
-} dm_status_mirror_health_t;
-
-struct dm_status_mirror {
- uint64_t total_regions;
- uint64_t insync_regions;
- uint32_t dev_count; /* # of devs[] elements (<= 8) */
- struct {
- dm_status_mirror_health_t health;
- uint32_t major;
- uint32_t minor;
- } *devs; /* array with individual legs */
- const char *log_type; /* core, disk,.... */
- uint32_t log_count; /* # of logs[] elements */
- struct {
- dm_status_mirror_health_t health;
- uint32_t major;
- uint32_t minor;
- } *logs; /* array with individual logs */
-};
-
-int dm_get_status_mirror(struct dm_pool *mem, const char *params,
- struct dm_status_mirror **status);
-
-/* Parse params from STATUS call for raid target */
-struct dm_status_raid {
- uint64_t reserved;
- uint64_t total_regions; /* sectors */
- uint64_t insync_regions; /* sectors */
- uint64_t mismatch_count;
- uint32_t dev_count;
- char *raid_type;
- /* A - alive, a - alive not in-sync, D - dead/failed */
- char *dev_health;
- /* idle, frozen, resync, recover, check, repair */
- char *sync_action;
- uint64_t data_offset; /* RAID out-of-place reshaping */
-};
-
-int dm_get_status_raid(struct dm_pool *mem, const char *params,
- struct dm_status_raid **status);
-
-/* Parse params from STATUS call for cache target */
-struct dm_status_cache {
- uint64_t version; /* zero for now */
-
- uint32_t metadata_block_size; /* in 512B sectors */
- uint32_t block_size; /* AKA 'chunk_size' */
-
- uint64_t metadata_used_blocks;
- uint64_t metadata_total_blocks;
-
- uint64_t used_blocks;
- uint64_t dirty_blocks;
- uint64_t total_blocks;
-
- uint64_t read_hits;
- uint64_t read_misses;
- uint64_t write_hits;
- uint64_t write_misses;
-
- uint64_t demotions;
- uint64_t promotions;
-
- uint64_t feature_flags; /* DM_CACHE_FEATURE_? */
-
- int core_argc;
- char **core_argv;
-
- char *policy_name;
- int policy_argc;
- char **policy_argv;
-
- unsigned error : 1; /* detected error (switches to fail soon) */
- unsigned fail : 1; /* all I/O fails */
- unsigned needs_check : 1; /* metadata needs check */
- unsigned read_only : 1; /* metadata may not be changed */
- uint32_t reserved : 28;
-};
-
-int dm_get_status_cache(struct dm_pool *mem, const char *params,
- struct dm_status_cache **status);
-
-/*
- * Parse params from STATUS call for snapshot target
- *
- * Snapshot target's format:
- * <= 1.7.0: <used_sectors>/<total_sectors>
- * >= 1.8.0: <used_sectors>/<total_sectors> <metadata_sectors>
- */
-struct dm_status_snapshot {
- uint64_t used_sectors; /* in 512b units */
- uint64_t total_sectors;
- uint64_t metadata_sectors;
- unsigned has_metadata_sectors : 1; /* set when metadata_sectors is present */
- unsigned invalid : 1; /* set when snapshot is invalidated */
- unsigned merge_failed : 1; /* set when snapshot merge failed */
- unsigned overflow : 1; /* set when snapshot overflows */
-};
-
-int dm_get_status_snapshot(struct dm_pool *mem, const char *params,
- struct dm_status_snapshot **status);
-
-/* Parse params from STATUS call for thin_pool target */
-typedef enum {
- DM_THIN_DISCARDS_IGNORE,
- DM_THIN_DISCARDS_NO_PASSDOWN,
- DM_THIN_DISCARDS_PASSDOWN
-} dm_thin_discards_t;
-
-struct dm_status_thin_pool {
- uint64_t transaction_id;
- uint64_t used_metadata_blocks;
- uint64_t total_metadata_blocks;
- uint64_t used_data_blocks;
- uint64_t total_data_blocks;
- uint64_t held_metadata_root;
- uint32_t read_only; /* metadata may not be changed */
- dm_thin_discards_t discards;
- uint32_t fail : 1; /* all I/O fails */
- uint32_t error_if_no_space : 1; /* otherwise queue_if_no_space */
- uint32_t out_of_data_space : 1; /* metadata may be changed, but data may not be allocated (no rw) */
- uint32_t needs_check : 1; /* metadata needs check */
- uint32_t error : 1; /* detected error (switches to fail soon) */
- uint32_t reserved : 27;
-};
-
-int dm_get_status_thin_pool(struct dm_pool *mem, const char *params,
- struct dm_status_thin_pool **status);
-
-/* Parse params from STATUS call for thin target */
-struct dm_status_thin {
- uint64_t mapped_sectors;
- uint64_t highest_mapped_sector;
- uint32_t fail : 1; /* Thin volume fails I/O */
- uint32_t reserved : 31;
-};
-
-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
- * and obtaining and manipulating current counter values from the
- * kernel. Methods are provided to return baisc count values and to
- * derive time-based metrics when a suitable interval estimate is
- * provided.
- *
- * Internally the dm_stats handle contains a pointer to a table of one
- * or more dm_stats_region objects representing the regions registered
- * with the dm_stats_create_region() method. These in turn point to a
- * table of one or more dm_stats_counters objects containing the
- * counter sets for each defined area within the region:
- *
- * dm_stats->dm_stats_region[nr_regions]->dm_stats_counters[nr_areas]
- *
- * This structure is private to the library and may change in future
- * versions: all users should make use of the public interface and treat
- * the dm_stats type as an opaque handle.
- *
- * Regions and counter sets are stored in order of increasing region_id.
- * Depending on region specifications and the sequence of create and
- * delete operations this may not correspond to increasing sector
- * number: users of the library should not assume that this is the case
- * unless region creation is deliberately managed to ensure this (by
- * always creating regions in strict order of ascending sector address).
- *
- * Regions may also overlap so the same sector range may be included in
- * more than one region or area: applications should be prepared to deal
- * with this or manage regions such that it does not occur.
- */
-struct dm_stats;
-
-/*
- * Histogram handle.
- *
- * A histogram object represents the latency histogram values and bin
- * boundaries of the histogram associated with a particular area.
- *
- * Operations on the handle allow the number of bins, bin boundaries,
- * counts and relative proportions to be obtained as well as the
- * conversion of a histogram or its bounds to a compact string
- * representation.
- */
-struct dm_histogram;
-
-/*
- * 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.
- *
- * If program_id is NULL or the empty string a program_id will be
- * automatically set to the value contained in /proc/self/comm.
- */
-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);
-
-/*
- * Bind a dm_stats handle to the device backing the file referenced
- * by the specified file descriptor.
- *
- * File descriptor fd must reference a regular file, open for reading,
- * in a local file system, backed by a device-mapper device, that
- * supports the FIEMAP ioctl, and that returns data describing the
- * physical location of extents.
- */
-int dm_stats_bind_from_fd(struct dm_stats *dms, int fd);
-/*
- * Test whether the running kernel supports the precise_timestamps
- * feature. Presence of this feature also implies histogram support.
- * The library will check this call internally and fails any attempt
- * to use nanosecond counters or histograms on kernels that fail to
- * meet this check.
- */
-int dm_message_supports_precise_timestamps(void);
-
-/*
- * Precise timetamps and histogram support.
- *
- * Test for the presence of precise_timestamps and histogram support.
- */
-int dm_stats_driver_supports_precise(void);
-int dm_stats_driver_supports_histogram(void);
-
-/*
- * Returns 1 if the specified region has the precise_timestamps feature
- * enabled (i.e. produces nanosecond-precision counter values) or 0 for
- * a region using the default milisecond precision.
- */
-int dm_stats_get_region_precise_timestamps(const struct dm_stats *dms,
- uint64_t region_id);
-
-/*
- * Returns 1 if the region at the current cursor location has the
- * precise_timestamps feature enabled (i.e. produces
- * nanosecond-precision counter values) or 0 for a region using the
- * default milisecond precision.
- */
-int dm_stats_get_current_region_precise_timestamps(const struct dm_stats *dms);
-
-#define DM_STATS_ALL_PROGRAMS ""
-/*
- * Parse the response from a @stats_list message. dm_stats_list will
- * allocate the necessary dm_stats and dm_stats region structures from
- * the embedded dm_pool. No counter data will be obtained (the counters
- * members of dm_stats_region objects are set to NULL).
- *
- * 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
- * the argument is NULL then the default program_id associated with the
- * dm_stats handle will be used. Passing the special value
- * DM_STATS_ALL_PROGRAMS will cause all regions to be queried
- * regardless of region program_id.
- */
-int dm_stats_list(struct dm_stats *dms, const char *program_id);
-
-#define DM_STATS_REGIONS_ALL UINT64_MAX
-/*
- * 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
- * the argument is NULL then the default program_id associated with the
- * dm_stats handle will be used. Passing the special value
- * DM_STATS_ALL_PROGRAMS will cause all regions to be queried
- * regardless of region program_id.
- *
- * Passing the special value DM_STATS_REGIONS_ALL as the region_id
- * argument will attempt to retrieve all regions selected by the
- * program_id argument.
- *
- * 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.
- * Passing zero for both start and len will create a region spanning
- * the entire device.
- *
- * Step determines how to subdivide the region into discrete counter
- * sets: a positive value specifies the size of areas into which the
- * region should be split while a negative value will split the region
- * into a number of areas equal to the absolute value of step:
- *
- * - a region with one area spanning the entire device:
- *
- * dm_stats_create_region(dms, 0, 0, -1, p, a);
- *
- * - a region with areas of 1MiB:
- *
- * dm_stats_create_region(dms, 0, 0, 1 << 11, p, a);
- *
- * - one 1MiB region starting at 1024 sectors with two areas:
- *
- * dm_stats_create_region(dms, 1024, 1 << 11, -2, p, a);
- *
- * If precise is non-zero attempt to create a region with nanosecond
- * precision counters using the kernel precise_timestamps feature.
- *
- * precise - A flag to request nanosecond precision counters
- * to be used for this region.
- *
- * histogram_bounds - specify the boundaries of a latency histogram to
- * be tracked for the region. The values are expressed as an array of
- * uint64_t terminated with a zero. Values must be in order of ascending
- * magnitude and specify the upper bounds of successive histogram bins
- * in nanoseconds (with an implicit lower bound of zero on the first bin
- * and an implicit upper bound of infinity on the final bin). For
- * example:
- *
- * uint64_t bounds_ary[] = { 1000, 2000, 3000, 0 };
- *
- * Specifies a histogram with four bins: 0-1000ns, 1000-2000ns,
- * 2000-3000ns and >3000ns.
- *
- * The smallest latency value that can be tracked for a region not using
- * precise_timestamps is 1ms: attempting to create a region with
- * histogram boundaries < 1ms will cause the precise_timestamps feature
- * to be enabled for that region automatically if it was not requested
- * explicitly.
- *
- * 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.
- *
- * user_data is an optional string argument that is added to the
- * content of the aux_data field stored with the statistics region by
- * the kernel.
- *
- * The library may also use this space internally, for example, to
- * store a group descriptor or other metadata: in this case the
- * library will strip any internal data fields from the value before
- * it is returned via a call to dm_stats_get_region_aux_data().
- *
- * The user data stored is not accessed by the library or kernel and
- * may be used to store an arbitrary data word (embedded whitespace is
- * not permitted).
- *
- * An application using both the library and direct access to the
- * @stats_list device-mapper message may see the internal values stored
- * in this field by the library. In such cases any string up to and
- * including the first '#' in the field must be treated as an opaque
- * value and preserved across any external modification of aux_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,
- int precise, struct dm_histogram *bounds,
- const char *program_id, const char *user_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.
- */
-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).
- *
- * 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
- * numeric form accessible via the dm_stats_get_*() counter access
- * methods.
- *
- * A subset of the data lines may be requested by setting the
- * 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).
- */
-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
- * 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).
- *
- * Always returns zero on an empty handle.
- */
-uint64_t dm_stats_get_nr_regions(const struct dm_stats *dms);
-
-/*
- * Determine the number of groups contained in a dm_stats handle
- * following a dm_stats_list() or dm_stats_populate() call.
- *
- * The value returned is the number of registered groups visible with the
- * progam_id value used for the list or populate operation and may not be
- * equal to the highest present group_id (either due to program_id
- * filtering or gaps in the sequence of group_id values).
- *
- * Always returns zero on an empty handle.
- */
-uint64_t dm_stats_get_nr_groups(const struct dm_stats *dms);
-
-/*
- * Test whether region_id is present in this dm_stats handle.
- */
-int dm_stats_region_present(const 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(const struct dm_stats *dms,
- uint64_t region_id);
-
-/*
- * Returns the total number of areas (counter sets) in all regions of the
- * given dm_stats object.
- */
-uint64_t dm_stats_get_nr_areas(const struct dm_stats *dms);
-
-/*
- * Test whether group_id is present in this dm_stats handle.
- */
-int dm_stats_group_present(const struct dm_stats *dms, uint64_t group_id);
-
-/*
- * Return the number of bins in the histogram configuration for the
- * specified region or zero if no histogram specification is configured.
- * Valid following a dm_stats_list() or dm_stats_populate() operation.
- */
-int dm_stats_get_region_nr_histogram_bins(const struct dm_stats *dms,
- uint64_t region_id);
-
-/*
- * Parse a histogram string with optional unit suffixes into a
- * dm_histogram bounds description.
- *
- * A histogram string is a string of numbers "n1,n2,n3,..." that
- * represent the boundaries of a histogram. The first and final bins
- * have implicit lower and upper bounds of zero and infinity
- * respectively and boundary values must occur in order of ascending
- * magnitude. Unless a unit suffix is given all values are specified in
- * nanoseconds.
- *
- * For example, if bounds_str="300,600,900", the region will be created
- * with a histogram containing four bins. Each report will include four
- * numbers a:b:c:d. a is the number of requests that took between 0 and
- * 300ns to complete, b is the number of requests that took 300-600ns to
- * complete, c is the number of requests that took 600-900ns to complete
- * and d is the number of requests that took more than 900ns to
- * complete.
- *
- * An optional unit suffix of 's', 'ms', 'us', or 'ns' may be used to
- * specify units of seconds, miliseconds, microseconds, or nanoseconds:
- *
- * bounds_str="1ns,1us,1ms,1s"
- * bounds_str="500us,1ms,1500us,2ms"
- * bounds_str="200ms,400ms,600ms,800ms,1s"
- *
- * The smallest valid unit of time for a histogram specification depends
- * on whether the region uses precise timestamps: for a region with the
- * default milisecond precision the smallest possible histogram boundary
- * magnitude is one milisecond: attempting to use a histogram with a
- * boundary less than one milisecond when creating a region will cause
- * the region to be created with the precise_timestamps feature enabled.
- *
- * On sucess a pointer to the struct dm_histogram representing the
- * bounds values is returned, or NULL in the case of error. The returned
- * pointer should be freed using dm_free() when no longer required.
- */
-struct dm_histogram *dm_histogram_bounds_from_string(const char *bounds_str);
-
-/*
- * Parse a zero terminated array of uint64_t into a dm_histogram bounds
- * description.
- *
- * Each value in the array specifies the upper bound of a bin in the
- * latency histogram in nanoseconds. Values must appear in ascending
- * order of magnitude.
- *
- * The smallest valid unit of time for a histogram specification depends
- * on whether the region uses precise timestamps: for a region with the
- * default milisecond precision the smallest possible histogram boundary
- * magnitude is one milisecond: attempting to use a histogram with a
- * boundary less than one milisecond when creating a region will cause
- * the region to be created with the precise_timestamps feature enabled.
- */
-struct dm_histogram *dm_histogram_bounds_from_uint64(const uint64_t *bounds);
-
-/*
- * Destroy the histogram bounds array obtained from a call to
- * dm_histogram_bounds_from_string().
- */
-void dm_histogram_bounds_destroy(struct dm_histogram *bounds);
-
-/*
- * Destroy a dm_stats object and all associated regions, counter
- * sets and histograms.
- */
-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.
- *
- * The interval is used to calculate time-based metrics from the basic
- * counter data: an interval must be set before calling any of the
- * metric methods.
- *
- * For best accuracy the duration should be measured and updated at the
- * 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.
- */
-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);
-
-/*
- * Retrieve the configured sampling interval in either nanoseconds or
- * milliseconds.
- */
-uint64_t dm_stats_get_sampling_interval_ns(const struct dm_stats *dms);
-uint64_t dm_stats_get_sampling_interval_ms(const 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.
- *
- * Use with caution! Most users of the library should set a valid,
- * non-NULL program_id for every statistics region created. Failing to
- * do so may result in confusing state when multiple programs are
- * creating and managing statistics regions.
- *
- * All users of the library are encouraged to choose an unambiguous,
- * unique program_id: this could be based on PID (for programs that
- * create, report, and delete regions in a single process), session id,
- * executable name, or some other distinguishing string.
- *
- * Use of the empty string as a program_id does not simplify use of the
- * library or the command line tools and use of this value is strongly
- * discouraged.
- */
-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.
- *
- * All values are returned in units of 512b sectors.
- */
-int dm_stats_get_region_start(const struct dm_stats *dms, uint64_t *start,
- uint64_t region_id);
-
-int dm_stats_get_region_len(const struct dm_stats *dms, uint64_t *len,
- uint64_t region_id);
-
-int dm_stats_get_region_area_len(const struct dm_stats *dms,
- uint64_t *len, uint64_t region_id);
-
-/*
- * Area properties: start, offset 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 of an area is a function of the area_id and the containing
- * region's start and area length: it gives the absolute offset into the
- * containing device of the beginning of the area.
- *
- * The offset expresses the area's relative offset into the current
- * region. I.e. the area start minus the start offset of the containing
- * region.
- *
- * All values are returned in units of 512b sectors.
- */
-int dm_stats_get_area_start(const struct dm_stats *dms, uint64_t *start,
- uint64_t region_id, uint64_t area_id);
-
-int dm_stats_get_area_offset(const struct dm_stats *dms, uint64_t *offset,
- uint64_t region_id, uint64_t area_id);
-
-/*
- * Retrieve program_id and user aux_data for a specific region.
- *
- * Only valid following a call to dm_stats_list().
- */
-
-/*
- * Retrieve program_id for the specified region.
- *
- * 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.
- */
-const char *dm_stats_get_region_program_id(const struct dm_stats *dms,
- uint64_t region_id);
-
-/*
- * Retrieve user aux_data set for the specified region. This function
- * will return any stored user aux_data as a string in the memory
- * pointed to by the aux_data argument.
- *
- * Any library internal aux_data fields, such as DMS_GROUP descriptors,
- * are stripped before the value is returned.
- *
- * 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.
- */
-const char *dm_stats_get_region_aux_data(const struct dm_stats *dms,
- uint64_t region_id);
-
-typedef enum {
- DM_STATS_OBJECT_TYPE_NONE,
- DM_STATS_OBJECT_TYPE_AREA,
- DM_STATS_OBJECT_TYPE_REGION,
- DM_STATS_OBJECT_TYPE_GROUP
-} dm_stats_obj_type_t;
-
-/*
- * Statistics cursor
- *
- * A dm_stats handle maintains an optional cursor into the statistics
- * tables that it stores. Iterators are provided to visit each region,
- * area, or group in a handle and accessor methods are provided to
- * obtain properties and values for the object at the current cursor
- * position.
- *
- * Using the cursor simplifies walking all regions or groups when
- * the tables are sparse (i.e. contains some present and some
- * non-present region_id or group_id values either due to program_id
- * filtering or the ordering of region and group creation and deletion).
- *
- * Simple macros are provided to visit each area, region, or group,
- * contained in a handle and applications are encouraged to use these
- * where possible.
- */
-
-/*
- * Walk flags are used to initialise a dm_stats handle's cursor control
- * and to select region or group aggregation when calling a metric or
- * counter property method with immediate group, region, and area ID
- * values.
- *
- * Walk flags are stored in the uppermost word of a uint64_t so that
- * a region_id or group_id may be encoded in the lower bits. This
- * allows an aggregate region_id or group_id to be specified when
- * retrieving counter or metric values.
- *
- * Flags may be ORred together when used to initialise a dm_stats_walk:
- * the resulting walk will visit instance of each type specified by
- * the flag combination.
- */
-#define DM_STATS_WALK_AREA 0x1000000000000ULL
-#define DM_STATS_WALK_REGION 0x2000000000000ULL
-#define DM_STATS_WALK_GROUP 0x4000000000000ULL
-
-#define DM_STATS_WALK_ALL 0x7000000000000ULL
-#define DM_STATS_WALK_DEFAULT (DM_STATS_WALK_AREA | DM_STATS_WALK_REGION)
-
-/*
- * Skip regions from a DM_STATS_WALK_REGION that contain only a single
- * area: in this case the region's aggregate values are identical to
- * the values of the single contained area. Setting this flag will
- * suppress these duplicate entries during a dm_stats_walk_* with the
- * DM_STATS_WALK_REGION flag set.
- */
-#define DM_STATS_WALK_SKIP_SINGLE_AREA 0x8000000000000ULL
-
-/*
- * Initialise the cursor control of a dm_stats handle for the specified
- * walk type(s). Including a walk flag in the flags argument will cause
- * any subsequent walk to visit that type of object (until the next
- * call to dm_stats_walk_init()).
- */
-int dm_stats_walk_init(struct dm_stats *dms, uint64_t flags);
-
-/*
- * Set the cursor of a dm_stats handle to address the first present
- * group, region, or area of the currently configured walk. 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. If the end of
- * the region, area, or group tables is reached a subsequent call to
- * dm_stats_walk_end() will return 1 and dm_stats_object_type() called
- * on the location will return DM_STATS_OBJECT_TYPE_NONE,
- */
-void dm_stats_walk_next(struct dm_stats *dms);
-
-/*
- * Force the statistics cursor to advance to the next region. This will
- * stop any in-progress area walk (by clearing DM_STATS_WALK_AREA) and
- * advance the cursor to the next present region, the first present
- * group (if DM_STATS_GROUP_WALK is set), or to the end. In this case a
- * subsequent call to dm_stats_walk_end() will return 1 and a call to
- * dm_stats_object_type() for the location will return
- * DM_STATS_OBJECT_TYPE_NONE.
- */
-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);
-
-/*
- * Return the type of object at the location specified by 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 the
- * corresponding region or area identifier will be taken from the
- * current cursor location. If the cursor location or the value encoded
- * by region_id and area_id indicates an aggregate region or group,
- * this will be reflected in the value returned.
- */
-dm_stats_obj_type_t dm_stats_object_type(const struct dm_stats *dms,
- uint64_t region_id,
- uint64_t area_id);
-
-/*
- * Return the type of object at the current stats cursor location.
- */
-dm_stats_obj_type_t dm_stats_current_object_type(const 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_init((dms), DM_STATS_WALK_REGION), \
- 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_init((dms), DM_STATS_WALK_AREA), \
- dm_stats_walk_start((dms)); \
- !dm_stats_walk_end((dms)); dm_stats_walk_next((dms)))
-
-/*
- * Iterate over the regions table visiting each group. Metric and
- * counter methods will return values for the group.
- *
- * If the group table is empty or unpopulated the loop body will not
- * be executed.
- */
-#define dm_stats_foreach_group(dms) \
-for (dm_stats_walk_init((dms), DM_STATS_WALK_GROUP), \
- 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) \
-do { \
- dm_stats_walk_start((dms)); \
- do
-
-/*
- * Start a 'while' style loop or end a 'do..while' loop iterating over the
- * regions contained in dm_stats handle 'dms'.
- */
-#define dm_stats_walk_while(dms) \
- while(!dm_stats_walk_end((dms))); \
-} while (0)
-
-/*
- * 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(const struct dm_stats *dms);
-
-/*
- * Retrieve the current values of the stats cursor.
- */
-uint64_t dm_stats_get_current_region(const struct dm_stats *dms);
-uint64_t dm_stats_get_current_area(const 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.
- */
-int dm_stats_get_current_region_start(const struct dm_stats *dms,
- uint64_t *start);
-
-int dm_stats_get_current_region_len(const struct dm_stats *dms,
- uint64_t *len);
-
-int dm_stats_get_current_region_area_len(const 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.
- */
-int dm_stats_get_current_area_start(const struct dm_stats *dms,
- uint64_t *start);
-
-int dm_stats_get_current_area_offset(const struct dm_stats *dms,
- uint64_t *offset);
-
-int dm_stats_get_current_area_len(const 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(const struct dm_stats *dms);
-
-/*
- * Return a pointer to the user aux_data string for the region at the
- * current cursor location.
- */
-const char *dm_stats_get_current_region_aux_data(const struct dm_stats *dms);
-
-/*
- * Statistics groups and data aggregation.
- */
-
-/*
- * Create a new group in stats handle dms from the group descriptor
- * passed in group. The group descriptor is a string containing a list
- * of region_id values that will be included in the group. The first
- * region_id found will be the group leader. Ranges of identifiers may
- * be expressed as "M-N", where M and N are the start and end region_id
- * values for the range.
- */
-int dm_stats_create_group(struct dm_stats *dms, const char *group,
- const char *alias, uint64_t *group_id);
-
-/*
- * Remove the specified group_id. If the remove argument is zero the
- * group will be removed but the regions that it contained will remain.
- * If remove is non-zero then all regions that belong to the group will
- * also be removed.
- */
-int dm_stats_delete_group(struct dm_stats *dms, uint64_t group_id, int remove);
-
-/*
- * Set an alias for this group or region. The alias will be returned
- * instead of the normal dm-stats name for this region or group.
- */
-int dm_stats_set_alias(struct dm_stats *dms, uint64_t group_id,
- const char *alias);
-
-/*
- * Returns a pointer to the currently configured alias for id, or the
- * name of the dm device the handle is bound to if no alias has been
- * set. The pointer will be freed automatically when a new alias is set
- * or when the stats handle is cleared.
- */
-const char *dm_stats_get_alias(const struct dm_stats *dms, uint64_t id);
-
-#define DM_STATS_GROUP_NONE UINT64_MAX
-/*
- * Return the group_id that the specified region_id belongs to, or the
- * special value DM_STATS_GROUP_NONE if the region does not belong
- * to any group.
- */
-uint64_t dm_stats_get_group_id(const struct dm_stats *dms, uint64_t region_id);
-
-/*
- * Store a pointer to a string describing the regions that are members
- * of the group specified by group_id in the memory pointed to by buf.
- * The string is in the same format as the 'group' argument to
- * dm_stats_create_group().
- *
- * The pointer does not need to be freed explicitly by the caller: it
- * will become invalid following a subsequent dm_stats_list(),
- * dm_stats_populate() or dm_stats_destroy() of the corresponding
- * dm_stats handle.
- */
-int dm_stats_get_group_descriptor(const struct dm_stats *dms,
- uint64_t group_id, char **buf);
-
-/*
- * Create regions that correspond to the extents of a file in the
- * filesystem and optionally place them into a group.
- *
- * File descriptor fd must reference a regular file, open for reading,
- * in a local file system that supports the FIEMAP ioctl, and that
- * returns data describing the physical location of extents.
- *
- * The file descriptor can be closed by the caller following the call
- * to dm_stats_create_regions_from_fd().
- *
- * Unless nogroup is non-zero the regions will be placed into a group
- * and the group alias set to the value supplied (if alias is NULL no
- * group alias will be assigned).
- *
- * On success the function returns a pointer to an array of uint64_t
- * containing the IDs of the newly created regions. The region_id
- * array is terminated by the value DM_STATS_REGION_NOT_PRESENT and
- * should be freed using dm_free() when no longer required.
- *
- * On error NULL is returned.
- *
- * Following a call to dm_stats_create_regions_from_fd() the handle
- * is guaranteed to be in a listed state, and to contain any region
- * and group identifiers created by the operation.
- *
- * The group_id for the new group is equal to the region_id value in
- * the first array element.
- */
-uint64_t *dm_stats_create_regions_from_fd(struct dm_stats *dms, int fd,
- int group, int precise,
- struct dm_histogram *bounds,
- const char *alias);
-/*
- * Update a group of regions that correspond to the extents of a file
- * in the filesystem, adding and removing regions to account for
- * allocation changes in the underlying file.
- *
- * File descriptor fd must reference a regular file, open for reading,
- * in a local file system that supports the FIEMAP ioctl, and that
- * returns data describing the physical location of extents.
- *
- * The file descriptor can be closed by the caller following the call
- * to dm_stats_update_regions_from_fd().
- *
- * On success the function returns a pointer to an array of uint64_t
- * containing the IDs of the updated regions (including any existing
- * regions that were not modified by the call).
- *
- * The region_id array is terminated by the special value
- * DM_STATS_REGION_NOT_PRESENT and should be freed using dm_free()
- * when no longer required.
- *
- * On error NULL is returned.
- *
- * Following a call to dm_stats_update_regions_from_fd() the handle
- * is guaranteed to be in a listed state, and to contain any region
- * and group identifiers created by the operation.
- *
- * This function cannot be used with file mapped regions that are
- * not members of a group: either group the regions, or remove them
- * and re-map them with dm_stats_create_regions_from_fd().
- */
-uint64_t *dm_stats_update_regions_from_fd(struct dm_stats *dms, int fd,
- uint64_t group_id);
-
-
-/*
- * The file map monitoring daemon can monitor files in two distinct
- * ways: the mode affects the behaviour of the daemon when a file
- * under monitoring is renamed or unlinked, and the conditions which
- * cause the daemon to terminate.
- *
- * In both modes, the daemon will always shut down when the group
- * being monitored is deleted.
- *
- * Follow inode:
- * The daemon follows the inode of the file, as it was at the time the
- * daemon started. The file descriptor referencing the file is kept
- * open at all times, and the daemon will exit when it detects that
- * the file has been unlinked and it is the last holder of a reference
- * to the file.
- *
- * This mode is useful if the file is expected to be renamed, or moved
- * within the file system, while it is being monitored.
- *
- * Follow path:
- * The daemon follows the path that was given on the daemon command
- * line. The file descriptor referencing the file is re-opened on each
- * iteration of the daemon, and the daemon will exit if no file exists
- * at this location (a tolerance is allowed so that a brief delay
- * between unlink() and creat() is permitted).
- *
- * This mode is useful if the file is updated by unlinking the original
- * and placing a new file at the same path.
- */
-
-typedef enum {
- DM_FILEMAPD_FOLLOW_INODE,
- DM_FILEMAPD_FOLLOW_PATH,
- DM_FILEMAPD_FOLLOW_NONE
-} dm_filemapd_mode_t;
-
-/*
- * Parse a string representation of a dmfilemapd mode.
- *
- * Returns a valid dm_filemapd_mode_t value on success, or
- * DM_FILEMAPD_FOLLOW_NONE on error.
- */
-dm_filemapd_mode_t dm_filemapd_mode_from_string(const char *mode_str);
-
-/*
- * Start the dmfilemapd filemap monitoring daemon for the specified
- * file descriptor, group, and file system path. The daemon will
- * monitor the file for allocation changes, and when a change is
- * detected, call dm_stats_update_regions_from_fd() to update the
- * mapped regions for the file.
- *
- * The path provided to dm_stats_start_filemapd() must be an absolute
- * path, and should reflect the path of 'fd' at the time that it was
- * opened.
- *
- * The mode parameter controls the behaviour of the daemon when the
- * file being monitored is unlinked or moved: see the comments for
- * dm_filemapd_mode_t for a full description and possible values.
- *
- * The daemon can be stopped at any time by sending SIGTERM to the
- * daemon pid.
- */
-int dm_stats_start_filemapd(int fd, uint64_t group_id, const char *path,
- dm_filemapd_mode_t mode, unsigned foreground,
- unsigned verbose);
-
-/*
- * Call this to actually run the ioctl.
- */
-int dm_task_run(struct dm_task *dmt);
-
-/*
- * The errno from the last device-mapper ioctl performed by dm_task_run.
- */
-int dm_task_get_errno(struct dm_task *dmt);
-
-/*
- * Call this to make or remove the device nodes associated with previously
- * issued commands.
- */
-void dm_task_update_nodes(void);
-
-/*
- * Mangling support
- *
- * Character whitelist: 0-9, A-Z, a-z, #+-.:=@_
- * HEX mangling format: \xNN, NN being the hex value of the character.
- * (whitelist and format supported by udev)
-*/
-typedef enum {
- DM_STRING_MANGLING_NONE, /* do not mangle at all */
- DM_STRING_MANGLING_AUTO, /* mangle only if not already mangled with hex, error when mixed */
- DM_STRING_MANGLING_HEX /* always mangle with hex encoding, no matter what the input is */
-} dm_string_mangling_t;
-
-/*
- * Set/get mangling mode used for device-mapper names and uuids.
- */
-int dm_set_name_mangling_mode(dm_string_mangling_t name_mangling);
-dm_string_mangling_t dm_get_name_mangling_mode(void);
-
-/*
- * Get mangled/unmangled form of the device-mapper name or uuid
- * irrespective of the global setting (set by dm_set_name_mangling_mode).
- * The name or uuid returned needs to be freed after use by calling dm_free!
- */
-char *dm_task_get_name_mangled(const struct dm_task *dmt);
-char *dm_task_get_name_unmangled(const struct dm_task *dmt);
-char *dm_task_get_uuid_mangled(const struct dm_task *dmt);
-char *dm_task_get_uuid_unmangled(const struct dm_task *dmt);
-
-/*
- * Configure the device-mapper directory
- */
-int dm_set_dev_dir(const char *dir);
-const char *dm_dir(void);
-
-/*
- * Configure sysfs directory, /sys by default
- */
-int dm_set_sysfs_dir(const char *dir);
-const char *dm_sysfs_dir(void);
-
-/*
- * Configure default UUID prefix string.
- * Conventionally this is a short capitalised prefix indicating the subsystem
- * that is managing the devices, e.g. "LVM-" or "MPATH-".
- * To support stacks of devices from different subsystems, recursive functions
- * stop recursing if they reach a device with a different prefix.
- */
-int dm_set_uuid_prefix(const char *uuid_prefix);
-const char *dm_uuid_prefix(void);
-
-/*
- * Determine whether a major number belongs to device-mapper or not.
- */
-int dm_is_dm_major(uint32_t major);
-
-/*
- * Get associated device name for given major and minor number by reading
- * the sysfs content. If this is a dm device, get associated dm name, the one
- * that appears in /dev/mapper. DM names could be resolved this way only if
- * kernel used >= 2.6.29, kernel name is found otherwise (e.g. dm-0).
- * If prefer_kernel_name is set, the kernel name is always preferred over
- * device-mapper name for dm devices no matter what the kernel version is.
- * For non-dm devices, we always get associated kernel name, e.g sda, md0 etc.
- * Returns 0 on error or if sysfs is not used (or configured incorrectly),
- * otherwise returns 1 and the supplied buffer holds the device name.
- */
-int dm_device_get_name(uint32_t major, uint32_t minor,
- int prefer_kernel_name,
- char *buf, size_t buf_size);
-
-/*
- * Determine whether a device has any holders (devices
- * using this device). If sysfs is not used (or configured
- * incorrectly), returns 0.
- */
-int dm_device_has_holders(uint32_t major, uint32_t minor);
-
-/*
- * Determine whether a device contains mounted filesystem.
- * If sysfs is not used (or configured incorrectly), returns 0.
- */
-int dm_device_has_mounted_fs(uint32_t major, uint32_t minor);
-
-
-/*
- * Callback is invoked for individal mountinfo lines,
- * minor, major and mount target are parsed and unmangled.
- */
-typedef int (*dm_mountinfo_line_callback_fn) (char *line, unsigned maj, unsigned min,
- char *target, void *cb_data);
-
-/*
- * Read all lines from /proc/self/mountinfo,
- * for each line calls read_fn callback.
- */
-int dm_mountinfo_read(dm_mountinfo_line_callback_fn read_fn, void *cb_data);
-
-/*
- * Initialise library
- */
-void dm_lib_init(void) __attribute__((constructor));
-
-/*
- * Release library resources
- */
-void dm_lib_release(void);
-void dm_lib_exit(void) __attribute__((destructor));
-
-/* An optimisation for clients making repeated calls involving dm ioctls */
-void dm_hold_control_dev(int hold_open);
-
-/*
- * Use NULL for all devices.
- */
-int dm_mknodes(const char *name);
-int dm_driver_version(char *version, size_t size);
-
-/******************************************************
- * Functions to build and manipulate trees of devices *
- ******************************************************/
-struct dm_tree;
-struct dm_tree_node;
-
-/*
- * Initialise an empty dependency tree.
- *
- * 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
- * parents/children.
- */
-struct dm_tree *dm_tree_create(void);
-void dm_tree_free(struct dm_tree *tree);
-
-/*
- * List of suffixes to be ignored when matching uuids against existing devices.
- */
-void dm_tree_set_optional_uuid_suffixes(struct dm_tree *dtree, const char **optional_uuid_suffixes);
-
-/*
- * Add nodes to the tree for a given device and all the devices it uses.
- */
-int dm_tree_add_dev(struct dm_tree *tree, uint32_t major, uint32_t minor);
-int dm_tree_add_dev_with_udev_flags(struct dm_tree *tree, uint32_t major,
- uint32_t minor, uint16_t udev_flags);
-
-/*
- * Add a new node to the tree if it doesn't already exist.
- */
-struct dm_tree_node *dm_tree_add_new_dev(struct dm_tree *tree,
- const char *name,
- const char *uuid,
- uint32_t major, uint32_t minor,
- int read_only,
- int clear_inactive,
- void *context);
-struct dm_tree_node *dm_tree_add_new_dev_with_udev_flags(struct dm_tree *tree,
- const char *name,
- const char *uuid,
- uint32_t major,
- uint32_t minor,
- int read_only,
- int clear_inactive,
- void *context,
- uint16_t udev_flags);
-
-/*
- * Search for a node in the tree.
- * Set major and minor to 0 or uuid to NULL to get the root node.
- */
-struct dm_tree_node *dm_tree_find_node(struct dm_tree *tree,
- uint32_t major,
- uint32_t minor);
-struct dm_tree_node *dm_tree_find_node_by_uuid(struct dm_tree *tree,
- const char *uuid);
-
-/*
- * Use this to walk through all children of a given node.
- * Set handle to NULL in first call.
- * Returns NULL after the last child.
- * Set inverted to use inverted tree.
- */
-struct dm_tree_node *dm_tree_next_child(void **handle,
- const struct dm_tree_node *parent,
- uint32_t inverted);
-
-/*
- * Get properties of a node.
- */
-const char *dm_tree_node_get_name(const struct dm_tree_node *node);
-const char *dm_tree_node_get_uuid(const struct dm_tree_node *node);
-const struct dm_info *dm_tree_node_get_info(const struct dm_tree_node *node);
-void *dm_tree_node_get_context(const struct dm_tree_node *node);
-/*
- * Returns 0 when node size and its children is unchanged.
- * Returns 1 when node or any of its children has increased size.
- * Rerurns -1 when node or any of its children has reduced size.
- */
-int dm_tree_node_size_changed(const struct dm_tree_node *dnode);
-
-/*
- * Returns the number of children of the given node (excluding the root node).
- * Set inverted for the number of parents.
- */
-int dm_tree_node_num_children(const struct dm_tree_node *node, uint32_t inverted);
-
-/*
- * Deactivate a device plus all dependencies.
- * Ignores devices that don't have a uuid starting with uuid_prefix.
- */
-int dm_tree_deactivate_children(struct dm_tree_node *dnode,
- const char *uuid_prefix,
- size_t uuid_prefix_len);
-/*
- * Preload/create a device plus all dependencies.
- * Ignores devices that don't have a uuid starting with uuid_prefix.
- */
-int dm_tree_preload_children(struct dm_tree_node *dnode,
- const char *uuid_prefix,
- size_t uuid_prefix_len);
-
-/*
- * Resume a device plus all dependencies.
- * Ignores devices that don't have a uuid starting with uuid_prefix.
- */
-int dm_tree_activate_children(struct dm_tree_node *dnode,
- const char *uuid_prefix,
- size_t uuid_prefix_len);
-
-/*
- * Suspend a device plus all dependencies.
- * Ignores devices that don't have a uuid starting with uuid_prefix.
- */
-int dm_tree_suspend_children(struct dm_tree_node *dnode,
- const char *uuid_prefix,
- size_t uuid_prefix_len);
-
-/*
- * 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);
-
-/*
- * Set the 'noflush' flag when suspending devices.
- * If the kernel supports it, instead of erroring outstanding I/O that
- * cannot be completed, the I/O is queued and resubmitted when the
- * device is resumed. This affects multipath devices when all paths
- * have failed and queue_if_no_path is set, and mirror devices when
- * block_on_error is set and the mirror log has failed.
- */
-void dm_tree_use_no_flush_suspend(struct dm_tree_node *dnode);
-
-/*
- * Retry removal of each device if not successful.
- */
-void dm_tree_retry_remove(struct dm_tree_node *dnode);
-
-/*
- * Is the uuid prefix present in the tree?
- * Only returns 0 if every node was checked successfully.
- * Returns 1 if the tree walk has to be aborted.
- */
-int dm_tree_children_use_uuid(struct dm_tree_node *dnode,
- const char *uuid_prefix,
- size_t uuid_prefix_len);
-
-/*
- * Construct tables for new nodes before activating them.
- */
-int dm_tree_node_add_snapshot_origin_target(struct dm_tree_node *dnode,
- uint64_t size,
- const char *origin_uuid);
-int dm_tree_node_add_snapshot_target(struct dm_tree_node *node,
- uint64_t size,
- const char *origin_uuid,
- const char *cow_uuid,
- int persistent,
- uint32_t chunk_size);
-int dm_tree_node_add_snapshot_merge_target(struct dm_tree_node *node,
- uint64_t size,
- const char *origin_uuid,
- const char *cow_uuid,
- const char *merge_uuid,
- uint32_t chunk_size);
-int dm_tree_node_add_error_target(struct dm_tree_node *node,
- uint64_t size);
-int dm_tree_node_add_zero_target(struct dm_tree_node *node,
- uint64_t size);
-int dm_tree_node_add_linear_target(struct dm_tree_node *node,
- uint64_t size);
-int dm_tree_node_add_striped_target(struct dm_tree_node *node,
- uint64_t size,
- uint32_t stripe_size);
-
-#define DM_CRYPT_IV_DEFAULT UINT64_C(-1) /* iv_offset == seg offset */
-/*
- * Function accepts one string in cipher specification
- * (chainmode and iv should be NULL because included in cipher string)
- * or
- * separate arguments which will be joined to "cipher-chainmode-iv"
- */
-int dm_tree_node_add_crypt_target(struct dm_tree_node *node,
- uint64_t size,
- const char *cipher,
- const char *chainmode,
- const char *iv,
- uint64_t iv_offset,
- 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 */
-#define DM_BLOCK_ON_ERROR 0x00000004 /* On error, suspend I/O */
-#define DM_CORELOG 0x00000008 /* In-memory log */
-
-int dm_tree_node_add_mirror_target_log(struct dm_tree_node *node,
- uint32_t region_size,
- unsigned clustered,
- const char *log_uuid,
- unsigned area_count,
- uint32_t flags);
-
-int dm_tree_node_add_raid_target(struct dm_tree_node *node,
- uint64_t size,
- const char *raid_type,
- uint32_t region_size,
- uint32_t stripe_size,
- uint64_t rebuilds,
- uint64_t flags);
-
-/*
- * Defines below are based on kernel's dm-cache.c defines
- * DM_CACHE_MIN_DATA_BLOCK_SIZE (32 * 1024 >> SECTOR_SHIFT)
- * DM_CACHE_MAX_DATA_BLOCK_SIZE (1024 * 1024 * 1024 >> SECTOR_SHIFT)
- */
-#define DM_CACHE_MIN_DATA_BLOCK_SIZE (UINT32_C(64))
-#define DM_CACHE_MAX_DATA_BLOCK_SIZE (UINT32_C(2097152))
-/*
- * Max supported size for cache pool metadata device.
- * Limitation is hardcoded into the kernel and bigger device sizes
- * are not accepted.
- *
- * Limit defined in drivers/md/dm-cache-metadata.h
- */
-#define DM_CACHE_METADATA_MAX_SECTORS DM_THIN_METADATA_MAX_SECTORS
-
-/*
- * Define number of elements in rebuild and writemostly arrays
- * 'of struct dm_tree_node_raid_params'.
- */
-
-struct dm_tree_node_raid_params {
- const char *raid_type;
-
- uint32_t stripes;
- uint32_t mirrors;
- uint32_t region_size;
- uint32_t stripe_size;
-
- /*
- * 'rebuilds' and 'writemostly' are bitfields that signify
- * which devices in the array are to be rebuilt or marked
- * writemostly. The kernel supports up to 253 legs.
- * We limit ourselves by choosing a lower value
- * for DEFAULT_RAID{1}_MAX_IMAGES in defaults.h.
- */
- uint64_t rebuilds;
- uint64_t writemostly;
- uint32_t writebehind; /* I/Os (kernel default COUNTER_MAX / 2) */
- uint32_t sync_daemon_sleep; /* ms (kernel default = 5sec) */
- uint32_t max_recovery_rate; /* kB/sec/disk */
- uint32_t min_recovery_rate; /* kB/sec/disk */
- uint32_t stripe_cache; /* sectors */
-
- uint64_t flags; /* [no]sync */
- uint32_t reserved2;
-};
-
-/*
- * Version 2 of above node raid params struct to keeep API compatibility.
- *
- * Extended for more than 64 legs (max 253 in the MD kernel runtime!),
- * delta_disks for disk add/remove reshaping,
- * data_offset for out-of-place reshaping
- * and data_copies for odd number of raid10 legs.
- */
-#define RAID_BITMAP_SIZE 4 /* 4 * 64 bit elements in rebuilds/writemostly arrays */
-struct dm_tree_node_raid_params_v2 {
- const char *raid_type;
-
- uint32_t stripes;
- uint32_t mirrors;
- uint32_t region_size;
- uint32_t stripe_size;
-
- int delta_disks; /* +/- number of disks to add/remove (reshaping) */
- int data_offset; /* data offset to set (out-of-place reshaping) */
-
- /*
- * 'rebuilds' and 'writemostly' are bitfields that signify
- * which devices in the array are to be rebuilt or marked
- * writemostly. The kernel supports up to 253 legs.
- * We limit ourselvs by choosing a lower value
- * for DEFAULT_RAID_MAX_IMAGES.
- */
- uint64_t rebuilds[RAID_BITMAP_SIZE];
- uint64_t writemostly[RAID_BITMAP_SIZE];
- uint32_t writebehind; /* I/Os (kernel default COUNTER_MAX / 2) */
- uint32_t data_copies; /* RAID # of data copies */
- uint32_t sync_daemon_sleep; /* ms (kernel default = 5sec) */
- uint32_t max_recovery_rate; /* kB/sec/disk */
- uint32_t min_recovery_rate; /* kB/sec/disk */
- uint32_t stripe_cache; /* sectors */
-
- uint64_t flags; /* [no]sync */
-};
-
-int dm_tree_node_add_raid_target_with_params(struct dm_tree_node *node,
- uint64_t size,
- const struct dm_tree_node_raid_params *p);
-
-/* Version 2 API function taking dm_tree_node_raid_params_v2 for aforementioned extensions. */
-int dm_tree_node_add_raid_target_with_params_v2(struct dm_tree_node *node,
- uint64_t size,
- const struct dm_tree_node_raid_params_v2 *p);
-
-/* Cache feature_flags */
-#define DM_CACHE_FEATURE_WRITEBACK 0x00000001
-#define DM_CACHE_FEATURE_WRITETHROUGH 0x00000002
-#define DM_CACHE_FEATURE_PASSTHROUGH 0x00000004
-#define DM_CACHE_FEATURE_METADATA2 0x00000008 /* cache v1.10 */
-
-struct dm_config_node;
-/*
- * Use for passing cache policy and all its args e.g.:
- *
- * policy_settings {
- * migration_threshold=2048
- * sequention_threashold=100
- * ...
- * }
- *
- * For policy without any parameters use NULL.
- */
-int dm_tree_node_add_cache_target(struct dm_tree_node *node,
- uint64_t size,
- uint64_t feature_flags, /* DM_CACHE_FEATURE_* */
- const char *metadata_uuid,
- const char *data_uuid,
- const char *origin_uuid,
- const char *policy_name,
- const struct dm_config_node *policy_settings,
- uint32_t data_block_size);
-
-/*
- * FIXME Add individual cache policy pairs <key> = value, like:
- * int dm_tree_node_add_cache_policy_arg(struct dm_tree_node *dnode,
- * const char *key, uint64_t value);
- */
-
-/*
- * Replicator operation mode
- * Note: API for Replicator is not yet stable
- */
-typedef enum {
- DM_REPLICATOR_SYNC, /* Synchronous replication */
- DM_REPLICATOR_ASYNC_WARN, /* Warn if async replicator is slow */
- DM_REPLICATOR_ASYNC_STALL, /* Stall replicator if not fast enough */
- DM_REPLICATOR_ASYNC_DROP, /* Drop sites out of sync */
- DM_REPLICATOR_ASYNC_FAIL, /* Fail replicator if slow */
- NUM_DM_REPLICATOR_MODES
-} dm_replicator_mode_t;
-
-int dm_tree_node_add_replicator_target(struct dm_tree_node *node,
- uint64_t size,
- const char *rlog_uuid,
- const char *rlog_type,
- unsigned rsite_index,
- dm_replicator_mode_t mode,
- uint32_t async_timeout,
- uint64_t fall_behind_data,
- uint32_t fall_behind_ios);
-
-int dm_tree_node_add_replicator_dev_target(struct dm_tree_node *node,
- uint64_t size,
- const char *replicator_uuid, /* Replicator control device */
- uint64_t rdevice_index,
- const char *rdev_uuid, /* Rimage device name/uuid */
- unsigned rsite_index,
- const char *slog_uuid,
- uint32_t slog_flags, /* Mirror log flags */
- uint32_t slog_region_size);
-/* End of Replicator API */
-
-/*
- * FIXME: Defines bellow are based on kernel's dm-thin.c defines
- * DATA_DEV_BLOCK_SIZE_MIN_SECTORS (64 * 1024 >> SECTOR_SHIFT)
- * DATA_DEV_BLOCK_SIZE_MAX_SECTORS (1024 * 1024 * 1024 >> SECTOR_SHIFT)
- */
-#define DM_THIN_MIN_DATA_BLOCK_SIZE (UINT32_C(128))
-#define DM_THIN_MAX_DATA_BLOCK_SIZE (UINT32_C(2097152))
-/*
- * Max supported size for thin pool metadata device (17112760320 bytes)
- * Limitation is hardcoded into the kernel and bigger device size
- * is not accepted.
- * drivers/md/dm-thin-metadata.h THIN_METADATA_MAX_SECTORS
- */
-#define DM_THIN_MAX_METADATA_SIZE (UINT64_C(255) * (1 << 14) * (4096 / (1 << 9)) - 256 * 1024)
-
-int dm_tree_node_add_thin_pool_target(struct dm_tree_node *node,
- uint64_t size,
- uint64_t transaction_id,
- const char *metadata_uuid,
- const char *pool_uuid,
- uint32_t data_block_size,
- uint64_t low_water_mark,
- unsigned skip_block_zeroing);
-
-/* Supported messages for thin provision target */
-typedef enum {
- DM_THIN_MESSAGE_CREATE_SNAP, /* device_id, origin_id */
- DM_THIN_MESSAGE_CREATE_THIN, /* device_id */
- DM_THIN_MESSAGE_DELETE, /* device_id */
- DM_THIN_MESSAGE_SET_TRANSACTION_ID, /* current_id, new_id */
- DM_THIN_MESSAGE_RESERVE_METADATA_SNAP, /* target version >= 1.1 */
- DM_THIN_MESSAGE_RELEASE_METADATA_SNAP, /* target version >= 1.1 */
-} dm_thin_message_t;
-
-int dm_tree_node_add_thin_pool_message(struct dm_tree_node *node,
- dm_thin_message_t type,
- uint64_t id1, uint64_t id2);
-
-/*
- * Set thin pool discard features
- * ignore - Disable support for discards
- * no_passdown - Don't pass discards down to underlying data device,
- * just remove the mapping
- * Feature is available since version 1.1 of the thin target.
- */
-int dm_tree_node_set_thin_pool_discard(struct dm_tree_node *node,
- unsigned ignore,
- unsigned no_passdown);
-/*
- * Set error if no space, instead of queueing for thin pool.
- */
-int dm_tree_node_set_thin_pool_error_if_no_space(struct dm_tree_node *node,
- unsigned error_if_no_space);
-/* Start thin pool with metadata in read-only mode */
-int dm_tree_node_set_thin_pool_read_only(struct dm_tree_node *node,
- unsigned read_only);
-/*
- * FIXME: Defines bellow are based on kernel's dm-thin.c defines
- * MAX_DEV_ID ((1 << 24) - 1)
- */
-#define DM_THIN_MAX_DEVICE_ID (UINT32_C((1 << 24) - 1))
-int dm_tree_node_add_thin_target(struct dm_tree_node *node,
- uint64_t size,
- const char *pool_uuid,
- uint32_t device_id);
-
-int dm_tree_node_set_thin_external_origin(struct dm_tree_node *node,
- const char *external_uuid);
-
-void dm_tree_node_set_udev_flags(struct dm_tree_node *node, uint16_t udev_flags);
-
-void dm_tree_node_set_presuspend_node(struct dm_tree_node *node,
- struct dm_tree_node *presuspend_node);
-
-int dm_tree_node_add_target_area(struct dm_tree_node *node,
- const char *dev_name,
- const char *dlid,
- uint64_t offset);
-
-/*
- * Only for temporarily-missing raid devices where changes are tracked.
- */
-int dm_tree_node_add_null_area(struct dm_tree_node *node, uint64_t offset);
-
-/*
- * Set readahead (in sectors) after loading the node.
- */
-void dm_tree_node_set_read_ahead(struct dm_tree_node *dnode,
- uint32_t read_ahead,
- uint32_t read_ahead_flags);
-
-/*
- * Set node callback hook before de/activation.
- * Callback is called before 'activation' of node for activation tree,
- * or 'deactivation' of node for deactivation tree.
- */
-typedef enum {
- DM_NODE_CALLBACK_PRELOADED, /* Node has preload deps */
- DM_NODE_CALLBACK_DEACTIVATED, /* Node is deactivated */
-} dm_node_callback_t;
-typedef int (*dm_node_callback_fn) (struct dm_tree_node *node,
- dm_node_callback_t type, void *cb_data);
-void dm_tree_node_set_callback(struct dm_tree_node *node,
- dm_node_callback_fn cb, void *cb_data);
-
-void dm_tree_set_cookie(struct dm_tree_node *node, uint32_t cookie);
-uint32_t dm_tree_get_cookie(struct dm_tree_node *node);
-
-/*****************************************************************************
- * Library functions
- *****************************************************************************/
-
-/*******************
- * Memory management
- *******************/
-
-/*
- * Never use these functions directly - use the macros following instead.
- */
-void *dm_malloc_wrapper(size_t s, const char *file, int line)
- __attribute__((__malloc__)) __attribute__((__warn_unused_result__));
-void *dm_malloc_aligned_wrapper(size_t s, size_t a, const char *file, int line)
- __attribute__((__malloc__)) __attribute__((__warn_unused_result__));
-void *dm_zalloc_wrapper(size_t s, const char *file, int line)
- __attribute__((__malloc__)) __attribute__((__warn_unused_result__));
-void *dm_realloc_wrapper(void *p, unsigned int s, const char *file, int line)
- __attribute__((__warn_unused_result__));
-void dm_free_wrapper(void *ptr);
-char *dm_strdup_wrapper(const char *s, const char *file, int line)
- __attribute__((__warn_unused_result__));
-int dm_dump_memory_wrapper(void);
-void dm_bounds_check_wrapper(void);
-
-#define dm_malloc(s) dm_malloc_wrapper((s), __FILE__, __LINE__)
-#define dm_malloc_aligned(s, a) dm_malloc_aligned_wrapper((s), (a), __FILE__, __LINE__)
-#define dm_zalloc(s) dm_zalloc_wrapper((s), __FILE__, __LINE__)
-#define dm_strdup(s) dm_strdup_wrapper((s), __FILE__, __LINE__)
-#define dm_free(p) dm_free_wrapper(p)
-#define dm_realloc(p, s) dm_realloc_wrapper((p), (s), __FILE__, __LINE__)
-#define dm_dump_memory() dm_dump_memory_wrapper()
-#define dm_bounds_check() dm_bounds_check_wrapper()
-
-/*
- * The pool allocator is useful when you are going to allocate
- * lots of memory, use the memory for a bit, and then free the
- * memory in one go. A surprising amount of code has this usage
- * profile.
- *
- * You should think of the pool as an infinite, contiguous chunk
- * of memory. The front of this chunk of memory contains
- * allocated objects, the second half is free. dm_pool_alloc grabs
- * the next 'size' bytes from the free half, in effect moving it
- * into the allocated half. This operation is very efficient.
- *
- * dm_pool_free frees the allocated object *and* all objects
- * allocated after it. It is important to note this semantic
- * difference from malloc/free. This is also extremely
- * efficient, since a single dm_pool_free can dispose of a large
- * complex object.
- *
- * dm_pool_destroy frees all allocated memory.
- *
- * eg, If you are building a binary tree in your program, and
- * know that you are only ever going to insert into your tree,
- * and not delete (eg, maintaining a symbol table for a
- * compiler). You can create yourself a pool, allocate the nodes
- * from it, and when the tree becomes redundant call dm_pool_destroy
- * (no nasty iterating through the tree to free nodes).
- *
- * eg, On the other hand if you wanted to repeatedly insert and
- * remove objects into the tree, you would be better off
- * allocating the nodes from a free list; you cannot free a
- * single arbitrary node with pool.
- */
-
-struct dm_pool;
-
-/* constructor and destructor */
-struct dm_pool *dm_pool_create(const char *name, size_t chunk_hint)
- __attribute__((__warn_unused_result__));
-void dm_pool_destroy(struct dm_pool *p);
-
-/* simple allocation/free routines */
-void *dm_pool_alloc(struct dm_pool *p, size_t s)
- __attribute__((__warn_unused_result__));
-void *dm_pool_alloc_aligned(struct dm_pool *p, size_t s, unsigned alignment)
- __attribute__((__warn_unused_result__));
-void dm_pool_empty(struct dm_pool *p);
-void dm_pool_free(struct dm_pool *p, void *ptr);
-
-/*
- * To aid debugging, a pool can be locked. Any modifications made
- * to the content of the pool while it is locked can be detected.
- * Default compilation is using a crc checksum to notice modifications.
- * The pool locking is using the mprotect with the compilation flag
- * DEBUG_ENFORCE_POOL_LOCKING to enforce the memory protection.
- */
-/* query pool lock status */
-int dm_pool_locked(struct dm_pool *p);
-/* mark pool as locked */
-int dm_pool_lock(struct dm_pool *p, int crc)
- __attribute__((__warn_unused_result__));
-/* mark pool as unlocked */
-int dm_pool_unlock(struct dm_pool *p, int crc)
- __attribute__((__warn_unused_result__));
-
-/*
- * Object building routines:
- *
- * These allow you to 'grow' an object, useful for
- * building strings, or filling in dynamic
- * arrays.
- *
- * It's probably best explained with an example:
- *
- * char *build_string(struct dm_pool *mem)
- * {
- * int i;
- * char buffer[16];
- *
- * if (!dm_pool_begin_object(mem, 128))
- * return NULL;
- *
- * for (i = 0; i < 50; i++) {
- * snprintf(buffer, sizeof(buffer), "%d, ", i);
- * if (!dm_pool_grow_object(mem, buffer, 0))
- * goto bad;
- * }
- *
- * // add null
- * if (!dm_pool_grow_object(mem, "\0", 1))
- * goto bad;
- *
- * return dm_pool_end_object(mem);
- *
- * bad:
- *
- * dm_pool_abandon_object(mem);
- * return NULL;
- *}
- *
- * So start an object by calling dm_pool_begin_object
- * with a guess at the final object size - if in
- * doubt make the guess too small.
- *
- * Then append chunks of data to your object with
- * dm_pool_grow_object. Finally get your object with
- * a call to dm_pool_end_object.
- *
- * Setting delta to 0 means it will use strlen(extra).
- */
-int dm_pool_begin_object(struct dm_pool *p, size_t hint);
-int dm_pool_grow_object(struct dm_pool *p, const void *extra, size_t delta);
-void *dm_pool_end_object(struct dm_pool *p);
-void dm_pool_abandon_object(struct dm_pool *p);
-
-/* utilities */
-char *dm_pool_strdup(struct dm_pool *p, const char *str)
- __attribute__((__warn_unused_result__));
-char *dm_pool_strndup(struct dm_pool *p, const char *str, size_t n)
- __attribute__((__warn_unused_result__));
-void *dm_pool_zalloc(struct dm_pool *p, size_t s)
- __attribute__((__warn_unused_result__));
-
-/******************
- * bitset functions
- ******************/
-
-typedef uint32_t *dm_bitset_t;
-
-dm_bitset_t dm_bitset_create(struct dm_pool *mem, unsigned num_bits);
-void dm_bitset_destroy(dm_bitset_t bs);
-
-int dm_bitset_equal(dm_bitset_t in1, dm_bitset_t in2);
-
-void dm_bit_and(dm_bitset_t out, dm_bitset_t in1, dm_bitset_t in2);
-void dm_bit_union(dm_bitset_t out, dm_bitset_t in1, dm_bitset_t in2);
-int dm_bit_get_first(dm_bitset_t bs);
-int dm_bit_get_next(dm_bitset_t bs, int last_bit);
-int dm_bit_get_last(dm_bitset_t bs);
-int dm_bit_get_prev(dm_bitset_t bs, int last_bit);
-
-#define DM_BITS_PER_INT (sizeof(int) * CHAR_BIT)
-
-#define dm_bit(bs, i) \
- ((bs)[((i) / DM_BITS_PER_INT) + 1] & (0x1 << ((i) & (DM_BITS_PER_INT - 1))))
-
-#define dm_bit_set(bs, i) \
- ((bs)[((i) / DM_BITS_PER_INT) + 1] |= (0x1 << ((i) & (DM_BITS_PER_INT - 1))))
-
-#define dm_bit_clear(bs, i) \
- ((bs)[((i) / DM_BITS_PER_INT) + 1] &= ~(0x1 << ((i) & (DM_BITS_PER_INT - 1))))
-
-#define dm_bit_set_all(bs) \
- memset((bs) + 1, -1, ((*(bs) / DM_BITS_PER_INT) + 1) * sizeof(int))
-
-#define dm_bit_clear_all(bs) \
- memset((bs) + 1, 0, ((*(bs) / DM_BITS_PER_INT) + 1) * sizeof(int))
-
-#define dm_bit_copy(bs1, bs2) \
- memcpy((bs1) + 1, (bs2) + 1, ((*(bs2) / DM_BITS_PER_INT) + 1) * sizeof(int))
-
-/*
- * Parse a string representation of a bitset into a dm_bitset_t. The
- * notation used is identical to the kernel bitmap parser (cpuset etc.)
- * and supports both lists ("1,2,3") and ranges ("1-2,5-8"). If the mem
- * parameter is NULL memory for the bitset will be allocated using
- * dm_malloc(). Otherwise the bitset will be allocated using the supplied
- * dm_pool.
- */
-dm_bitset_t dm_bitset_parse_list(const char *str, struct dm_pool *mem,
- size_t min_num_bits);
-
-/* Returns number of set bits */
-static inline unsigned hweight32(uint32_t i)
-{
- unsigned r = (i & 0x55555555) + ((i >> 1) & 0x55555555);
-
- r = (r & 0x33333333) + ((r >> 2) & 0x33333333);
- r = (r & 0x0F0F0F0F) + ((r >> 4) & 0x0F0F0F0F);
- r = (r & 0x00FF00FF) + ((r >> 8) & 0x00FF00FF);
- return (r & 0x0000FFFF) + ((r >> 16) & 0x0000FFFF);
-}
-
-/****************
- * hash functions
- ****************/
-
-struct dm_hash_table;
-struct dm_hash_node;
-
-typedef void (*dm_hash_iterate_fn) (void *data);
-
-struct dm_hash_table *dm_hash_create(unsigned size_hint)
- __attribute__((__warn_unused_result__));
-void dm_hash_destroy(struct dm_hash_table *t);
-void dm_hash_wipe(struct dm_hash_table *t);
-
-void *dm_hash_lookup(struct dm_hash_table *t, const char *key);
-int dm_hash_insert(struct dm_hash_table *t, const char *key, void *data);
-void dm_hash_remove(struct dm_hash_table *t, const char *key);
-
-void *dm_hash_lookup_binary(struct dm_hash_table *t, const void *key, uint32_t len);
-int dm_hash_insert_binary(struct dm_hash_table *t, const void *key, uint32_t len,
- void *data);
-void dm_hash_remove_binary(struct dm_hash_table *t, const void *key, uint32_t len);
-
-unsigned dm_hash_get_num_entries(struct dm_hash_table *t);
-void dm_hash_iter(struct dm_hash_table *t, dm_hash_iterate_fn f);
-
-char *dm_hash_get_key(struct dm_hash_table *t, struct dm_hash_node *n);
-void *dm_hash_get_data(struct dm_hash_table *t, struct dm_hash_node *n);
-struct dm_hash_node *dm_hash_get_first(struct dm_hash_table *t);
-struct dm_hash_node *dm_hash_get_next(struct dm_hash_table *t, struct dm_hash_node *n);
-
-/*
- * dm_hash_insert() replaces the value of an existing
- * entry with a matching key if one exists. Otherwise
- * it adds a new entry.
- *
- * dm_hash_insert_with_val() inserts a new entry if
- * another entry with the same key already exists.
- * val_len is the size of the data being inserted.
- *
- * If two entries with the same key exist,
- * (added using dm_hash_insert_allow_multiple), then:
- * . dm_hash_lookup() returns the first one it finds, and
- * dm_hash_lookup_with_val() returns the one with a matching
- * val_len/val.
- * . dm_hash_remove() removes the first one it finds, and
- * dm_hash_remove_with_val() removes the one with a matching
- * val_len/val.
- *
- * If a single entry with a given key exists, and it has
- * zero val_len, then:
- * . dm_hash_lookup() returns it
- * . dm_hash_lookup_with_val(val_len=0) returns it
- * . dm_hash_remove() removes it
- * . dm_hash_remove_with_val(val_len=0) removes it
- *
- * dm_hash_lookup_with_count() is a single call that will
- * both lookup a key's value and check if there is more
- * than one entry with the given key.
- *
- * (It is not meant to retrieve all the entries with the
- * given key. In the common case where a single entry exists
- * for the key, it is useful to have a single call that will
- * both look up the value and indicate if multiple values
- * exist for the key.)
- *
- * dm_hash_lookup_with_count:
- * . If no entries exist, the function returns NULL, and
- * the count is set to 0.
- * . If only one entry exists, the value of that entry is
- * returned and count is set to 1.
- * . If N entries exists, the value of the first entry is
- * returned and count is set to N.
- */
-
-void *dm_hash_lookup_with_val(struct dm_hash_table *t, const char *key,
- const void *val, uint32_t val_len);
-void dm_hash_remove_with_val(struct dm_hash_table *t, const char *key,
- const void *val, uint32_t val_len);
-int dm_hash_insert_allow_multiple(struct dm_hash_table *t, const char *key,
- const void *val, uint32_t val_len);
-void *dm_hash_lookup_with_count(struct dm_hash_table *t, const char *key, int *count);
-
-
-#define dm_hash_iterate(v, h) \
- for (v = dm_hash_get_first((h)); v; \
- v = dm_hash_get_next((h), v))
-
-/*********
- * selinux
- *********/
-
-/*
- * Obtain SELinux security context assigned for the path and set this
- * context for creating a new file system object. This security context
- * is global and it is used until reset to default policy behaviour
- * by calling 'dm_prepare_selinux_context(NULL, 0)'.
- */
-int dm_prepare_selinux_context(const char *path, mode_t mode);
-/*
- * Set SELinux context for existing file system object.
- */
-int dm_set_selinux_context(const char *path, mode_t mode);
-
-/*********************
- * string manipulation
- *********************/
-
-/*
- * Break up the name of a mapped device into its constituent
- * Volume Group, Logical Volume and Layer (if present).
- * If mem is supplied, the result is allocated from the mempool.
- * Otherwise the strings are changed in situ.
- */
-int dm_split_lvm_name(struct dm_pool *mem, const char *dmname,
- char **vgname, char **lvname, char **layer);
-
-/*
- * Destructively split buffer into NULL-separated words in argv.
- * Returns number of words.
- */
-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, ...)
- __attribute__ ((format(printf, 3, 4)));
-
-/*
- * Returns pointer to the last component of the path.
- */
-const char *dm_basename(const char *path);
-
-/*
- * Returns number of occurrences of 'c' in 'str' of length 'size'.
- */
-unsigned dm_count_chars(const char *str, size_t len, const int c);
-
-/*
- * Length of string after escaping double quotes and backslashes.
- */
-size_t dm_escaped_len(const char *str);
-
-/*
- * <vg>-<lv>-<layer> or if !layer just <vg>-<lv>.
- */
-char *dm_build_dm_name(struct dm_pool *mem, const char *vgname,
- const char *lvname, const char *layer);
-char *dm_build_dm_uuid(struct dm_pool *mem, const char *prefix, const char *lvid, const char *layer);
-
-/*
- * Copies a string, quoting double quotes with backslashes.
- */
-char *dm_escape_double_quotes(char *out, const char *src);
-
-/*
- * Undo quoting in situ.
- */
-void dm_unescape_double_quotes(char *src);
-
-/*
- * Unescape colons and "at" signs in situ and save the substrings
- * starting at the position of the first unescaped colon and the
- * first unescaped "at" sign. This is normally used to unescape
- * device names used as PVs.
- */
-void dm_unescape_colons_and_at_signs(char *src,
- char **substr_first_unquoted_colon,
- char **substr_first_unquoted_at_sign);
-
-/*
- * Replacement for strncpy() function.
- *
- * Copies no more than n bytes from string pointed by src to the buffer
- * pointed by dest and ensure string is finished with '\0'.
- * Returns 0 if the whole string does not fit.
- */
-int dm_strncpy(char *dest, const char *src, size_t n);
-
-/*
- * Recognize unit specifier in the 'units' arg and return a factor
- * representing that unit. If the 'units' contains a prefix with digits,
- * the 'units' is considered to be a custom unit.
- *
- * Also, set 'unit_type' output arg to the character that represents
- * the unit specified. The 'unit_type' character equals to the unit
- * character itself recognized in the 'units' arg for canonical units.
- * Otherwise, the 'unit_type' character is set to 'U' for custom unit.
- *
- * An example for k/K canonical units and 8k/8K custom units:
- *
- * units unit_type return value (factor)
- * k k 1024
- * K K 1000
- * 8k U 1024*8
- * 8K U 1000*8
- * etc...
- *
- * Recognized units:
- *
- * h/H - human readable (returns 1 for both)
- * b/B - byte (returns 1 for both)
- * s/S - sector (returns 512 for both)
- * k/K - kilo (returns 1024/1000 respectively)
- * m/M - mega (returns 1024^2/1000^2 respectively)
- * g/G - giga (returns 1024^3/1000^3 respectively)
- * t/T - tera (returns 1024^4/1000^4 respectively)
- * p/P - peta (returns 1024^5/1000^5 respectively)
- * e/E - exa (returns 1024^6/1000^6 respectively)
- *
- * Only one units character is allowed in the 'units' arg
- * if strict mode is enabled by 'strict' arg.
- *
- * The 'endptr' output arg, if not NULL, saves the pointer
- * in the 'units' string which follows the unit specifier
- * recognized (IOW the position where the parsing of the
- * unit specifier stopped).
- *
- * Returns the unit factor or 0 if no unit is recognized.
- */
-uint64_t dm_units_to_factor(const char *units, char *unit_type,
- int strict, const char **endptr);
-
-/*
- * Type of unit specifier used by dm_size_to_string().
- */
-typedef enum {
- DM_SIZE_LONG = 0, /* Megabyte */
- DM_SIZE_SHORT = 1, /* MB or MiB */
- DM_SIZE_UNIT = 2 /* M or m */
-} dm_size_suffix_t;
-
-/*
- * Convert a size (in 512-byte sectors) into a printable string using units of unit_type.
- * An upper-case unit_type indicates output units based on powers of 1000 are
- * required; a lower-case unit_type indicates powers of 1024.
- * For correct operation, unit_factor must be one of:
- * 0 - the correct value will be calculated internally;
- * or the output from dm_units_to_factor() corresponding to unit_type;
- * or 'u' or 'U', an arbitrary number of bytes to use as the power base.
- * Set include_suffix to 1 to include a suffix of suffix_type.
- * Set use_si_units to 0 for suffixes that don't distinguish between 1000 and 1024.
- * 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,
- dm_size_suffix_t suffix_type);
-
-/**************************
- * file/stream manipulation
- **************************/
-
-/*
- * Create a directory (with parent directories if necessary).
- * Returns 1 on success, 0 on failure.
- */
-int dm_create_dir(const char *dir);
-
-int dm_is_empty_dir(const char *dir);
-
-/*
- * Close a stream, with nicer error checking than fclose's.
- * Derived from gnulib's close-stream.c.
- *
- * Close "stream". Return 0 if successful, and EOF (setting errno)
- * otherwise. Upon failure, set errno to 0 if the error number
- * cannot be determined. Useful mainly for writable streams.
- */
-int dm_fclose(FILE *stream);
-
-/*
- * Returns size of a buffer which is allocated with dm_malloc.
- * Pointer to the buffer is stored in *buf.
- * Returns -1 on failure leaving buf undefined.
- */
-int dm_asprintf(char **buf, const char *format, ...)
- __attribute__ ((format(printf, 2, 3)));
-int dm_vasprintf(char **buf, const char *format, va_list ap)
- __attribute__ ((format(printf, 2, 0)));
-
-/*
- * create lockfile (pidfile) - create and lock a lock file
- * @lockfile: location of lock file
- *
- * Returns: 1 on success, 0 otherwise, errno is handled internally
- */
-int dm_create_lockfile(const char* lockfile);
-
-/*
- * Query whether a daemon is running based on its lockfile
- *
- * Returns: 1 if running, 0 if not
- */
-int dm_daemon_is_running(const char* lockfile);
-
-/*********************
- * regular expressions
- *********************/
-struct dm_regex;
-
-/*
- * Initialise an array of num patterns for matching.
- * Uses memory from mem.
- */
-struct dm_regex *dm_regex_create(struct dm_pool *mem, const char * const *patterns,
- unsigned num_patterns);
-
-/*
- * Match string s against the patterns.
- * Returns the index of the highest pattern in the array that matches,
- * or -1 if none match.
- */
-int dm_regex_match(struct dm_regex *regex, const char *s);
-
-/*
- * This is useful for regression testing only. The idea is if two
- * fingerprints are different, then the two dfas are certainly not
- * isomorphic. If two fingerprints _are_ the same then it's very likely
- * that the dfas are isomorphic.
- *
- * This function must be called before any matching is done.
- */
-uint32_t dm_regex_fingerprint(struct dm_regex *regex);
-
-/******************
- * percent handling
- ******************/
-/*
- * A fixed-point representation of percent values. One percent equals to
- * DM_PERCENT_1 as defined below. Values that are not multiples of DM_PERCENT_1
- * represent fractions, with precision of 1/1000000 of a percent. See
- * dm_percent_to_float for a conversion to a floating-point representation.
- *
- * You should always use dm_make_percent when building dm_percent_t values. The
- * implementation of dm_make_percent is biased towards the middle: it ensures that
- * the result is DM_PERCENT_0 or DM_PERCENT_100 if and only if this is the actual
- * value -- it never rounds any intermediate value (> 0 or < 100) to either 0
- * or 100.
-*/
-#define DM_PERCENT_CHAR '%'
-
-typedef enum {
- DM_PERCENT_0 = 0,
- DM_PERCENT_1 = 1000000,
- DM_PERCENT_100 = 100 * DM_PERCENT_1,
- DM_PERCENT_INVALID = -1,
- DM_PERCENT_FAILED = -2
-} dm_percent_range_t;
-
-typedef int32_t dm_percent_t;
-
-float dm_percent_to_float(dm_percent_t percent);
-/*
- * Return adjusted/rounded float for better percent value printing.
- * Function ensures for given precision of digits:
- * 100.0% returns only when the value is DM_PERCENT_100
- * for close smaller values rounds to nearest smaller value
- * 0.0% returns only for value DM_PERCENT_0
- * for close bigger values rounds to nearest bigger value
- * In all other cases returns same value as dm_percent_to_float()
- */
-float dm_percent_to_round_float(dm_percent_t percent, unsigned digits);
-dm_percent_t dm_make_percent(uint64_t numerator, uint64_t denominator);
-
-/********************
- * timestamp handling
- ********************/
-
-/*
- * Create a dm_timestamp object to use with dm_timestamp_get.
- */
-struct dm_timestamp *dm_timestamp_alloc(void);
-
-/*
- * Update dm_timestamp object to represent the current time.
- */
-int dm_timestamp_get(struct dm_timestamp *ts);
-
-/*
- * Copy a timestamp from ts_old to ts_new.
- */
-void dm_timestamp_copy(struct dm_timestamp *ts_new, struct dm_timestamp *ts_old);
-
-/*
- * 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
- */
-int dm_timestamp_compare(struct dm_timestamp *ts1, struct dm_timestamp *ts2);
-
-/*
- * Return the absolute difference in nanoseconds between
- * the dm_timestamp objects ts1 and ts2.
- *
- * Callers that need to know whether ts1 is before, equal to, or after ts2
- * in addition to the magnitude should use dm_timestamp_compare.
- */
-uint64_t dm_timestamp_delta(struct dm_timestamp *ts1, struct dm_timestamp *ts2);
-
-/*
- * Destroy a dm_timestamp object.
- */
-void dm_timestamp_destroy(struct dm_timestamp *ts);
-
-/*********************
- * reporting functions
- *********************/
-
-struct dm_report_object_type {
- uint32_t id; /* Powers of 2 */
- const char *desc;
- const char *prefix; /* field id string prefix (optional) */
- /* FIXME: convert to proper usage of const pointers here */
- void *(*data_fn)(void *object); /* callback from report_object() */
-};
-
-struct dm_report_field;
-
-/*
- * dm_report_field_type flags
- */
-#define DM_REPORT_FIELD_MASK 0x00000FFF
-#define DM_REPORT_FIELD_ALIGN_MASK 0x0000000F
-#define DM_REPORT_FIELD_ALIGN_LEFT 0x00000001
-#define DM_REPORT_FIELD_ALIGN_RIGHT 0x00000002
-#define DM_REPORT_FIELD_TYPE_MASK 0x00000FF0
-#define DM_REPORT_FIELD_TYPE_NONE 0x00000000
-#define DM_REPORT_FIELD_TYPE_STRING 0x00000010
-#define DM_REPORT_FIELD_TYPE_NUMBER 0x00000020
-#define DM_REPORT_FIELD_TYPE_SIZE 0x00000040
-#define DM_REPORT_FIELD_TYPE_PERCENT 0x00000080
-#define DM_REPORT_FIELD_TYPE_STRING_LIST 0x00000100
-#define DM_REPORT_FIELD_TYPE_TIME 0x00000200
-
-/* For use with reserved values only! */
-#define DM_REPORT_FIELD_RESERVED_VALUE_MASK 0x0000000F
-#define DM_REPORT_FIELD_RESERVED_VALUE_NAMED 0x00000001 /* only named value, less strict form of reservation */
-#define DM_REPORT_FIELD_RESERVED_VALUE_RANGE 0x00000002 /* value is range - low and high value defined */
-#define DM_REPORT_FIELD_RESERVED_VALUE_DYNAMIC_VALUE 0x00000004 /* value is computed in runtime */
-#define DM_REPORT_FIELD_RESERVED_VALUE_FUZZY_NAMES 0x00000008 /* value names are recognized in runtime */
-
-#define DM_REPORT_FIELD_TYPE_ID_LEN 32
-#define DM_REPORT_FIELD_TYPE_HEADING_LEN 32
-
-struct dm_report;
-struct dm_report_field_type {
- uint32_t type; /* object type id */
- uint32_t flags; /* DM_REPORT_FIELD_* */
- uint32_t offset; /* byte offset in the object */
- int32_t width; /* default width */
- /* string used to specify the field */
- const char id[DM_REPORT_FIELD_TYPE_ID_LEN];
- /* string printed in header */
- 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,
- void *private_data);
- const char *desc; /* description of the field */
-};
-
-/*
- * Per-field reserved value.
- */
-struct dm_report_field_reserved_value {
- /* field_num is the position of the field in 'fields'
- array passed to dm_report_init_with_selection */
- uint32_t field_num;
- /* the value is of the same type as the field
- identified by field_num */
- const void *value;
-};
-
-/*
- * Reserved value is a 'value' that is used directly if any of the 'names' is hit
- * or in case of fuzzy names, if such fuzzy name matches.
- *
- * If type is any of DM_REPORT_FIELD_TYPE_*, the reserved value is recognized
- * for all fields of that type.
- *
- * If type is DM_REPORT_FIELD_TYPE_NONE, the reserved value is recognized
- * for the exact field specified - hence the type of the value is automatically
- * the same as the type of the field itself.
- *
- * The array of reserved values is used to initialize reporting with
- * selection enabled (see also dm_report_init_with_selection function).
- */
-struct dm_report_reserved_value {
- const uint32_t type; /* DM_REPORT_FIELD_RESERVED_VALUE_* and DM_REPORT_FIELD_TYPE_* */
- const void *value; /* reserved value:
- uint64_t for DM_REPORT_FIELD_TYPE_NUMBER
- uint64_t for DM_REPORT_FIELD_TYPE_SIZE (number of 512-byte sectors)
- uint64_t for DM_REPORT_FIELD_TYPE_PERCENT
- const char* for DM_REPORT_FIELD_TYPE_STRING
- struct dm_report_field_reserved_value for DM_REPORT_FIELD_TYPE_NONE
- dm_report_reserved_handler* if DM_REPORT_FIELD_RESERVED_VALUE_{DYNAMIC_VALUE,FUZZY_NAMES} is used */
- const char **names; /* null-terminated array of static names for this reserved value */
- const char *description; /* description of the reserved value */
-};
-
-/*
- * Available actions for dm_report_reserved_value_handler.
- */
-typedef enum {
- DM_REPORT_RESERVED_PARSE_FUZZY_NAME,
- DM_REPORT_RESERVED_GET_DYNAMIC_VALUE,
-} dm_report_reserved_action_t;
-
-/*
- * Generic reserved value handler to process reserved value names and/or values.
- *
- * Actions and their input/output:
- *
- * DM_REPORT_RESERVED_PARSE_FUZZY_NAME
- * data_in: const char *fuzzy_name
- * data_out: const char *canonical_name, NULL if fuzzy_name not recognized
- *
- * DM_REPORT_RESERVED_GET_DYNAMIC_VALUE
- * data_in: const char *canonical_name
- * data_out: void *value, NULL if canonical_name not recognized
- *
- * All actions return:
- *
- * -1 if action not implemented
- * 0 on error
- * 1 on success
- */
-typedef int (*dm_report_reserved_handler) (struct dm_report *rh,
- struct dm_pool *mem,
- uint32_t field_num,
- dm_report_reserved_action_t action,
- const void *data_in,
- const void **data_out);
-
-/*
- * 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
- * selection processing (dm_report_reserved_handler instances) to avoid
- * recalculation of these values or to share values among calls.
- */
-int dm_report_value_cache_set(struct dm_report *rh, const char *name, const void *data);
-const void *dm_report_value_cache_get(struct dm_report *rh, const char *name);
-/*
- * dm_report_init output_flags
- */
-#define DM_REPORT_OUTPUT_MASK 0x000000FF
-#define DM_REPORT_OUTPUT_ALIGNED 0x00000001
-#define DM_REPORT_OUTPUT_BUFFERED 0x00000002
-#define DM_REPORT_OUTPUT_HEADINGS 0x00000004
-#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_MULTIPLE_TIMES 0x00000040
-
-struct dm_report *dm_report_init(uint32_t *report_types,
- const struct dm_report_object_type *types,
- const struct dm_report_field_type *fields,
- const char *output_fields,
- const char *output_separator,
- uint32_t output_flags,
- const char *sort_keys,
- void *private_data);
-struct dm_report *dm_report_init_with_selection(uint32_t *report_types,
- const struct dm_report_object_type *types,
- const struct dm_report_field_type *fields,
- const char *output_fields,
- const char *output_separator,
- uint32_t output_flags,
- const char *sort_keys,
- const char *selection,
- const struct dm_report_reserved_value reserved_values[],
- void *private_data);
-/*
- * Report an object, pass it through the selection criteria if they
- * are present and display the result on output if it passes the criteria.
- */
-int dm_report_object(struct dm_report *rh, void *object);
-/*
- * The same as dm_report_object, but display the result on output only if
- * 'do_output' arg is set. Also, save the result of selection in 'selected'
- * arg if it's not NULL (either 1 if the object passes, otherwise 0).
- */
-int dm_report_object_is_selected(struct dm_report *rh, void *object, int do_output, int *selected);
-
-/*
- * Compact report output so that if field value is empty for all rows in
- * the report, drop the field from output completely (including headers).
- * Compact output is applicable only if report is buffered, otherwise
- * this function has no effect.
- */
-int dm_report_compact_fields(struct dm_report *rh);
-
-/*
- * The same as dm_report_compact_fields, but for selected fields only.
- * The "fields" arg is comma separated list of field names (the same format
- * as used for "output_fields" arg in dm_report_init fn).
- */
-int dm_report_compact_given_fields(struct dm_report *rh, const char *fields);
-
-/*
- * Returns 1 if there is no data waiting to be output.
- */
-int dm_report_is_empty(struct dm_report *rh);
-
-/*
- * Destroy report content without doing output.
- */
-void dm_report_destroy_rows(struct dm_report *rh);
-
-int dm_report_output(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.
- */
-int dm_report_column_headings(struct dm_report *rh);
-
-void dm_report_free(struct dm_report *rh);
-
-/*
- * Prefix added to each field name with DM_REPORT_OUTPUT_FIELD_NAME_PREFIX
- */
-int dm_report_set_output_field_name_prefix(struct dm_report *rh,
- const char *report_prefix);
-
-int dm_report_set_selection(struct dm_report *rh, const char *selection);
-
-/*
- * Report functions are provided for simple data types.
- * They take care of allocating copies of the data.
- */
-int dm_report_field_string(struct dm_report *rh, struct dm_report_field *field,
- const char *const *data);
-int dm_report_field_string_list(struct dm_report *rh, struct dm_report_field *field,
- const struct dm_list *data, const char *delimiter);
-int dm_report_field_string_list_unsorted(struct dm_report *rh, struct dm_report_field *field,
- const struct dm_list *data, const char *delimiter);
-int dm_report_field_int32(struct dm_report *rh, struct dm_report_field *field,
- const int32_t *data);
-int dm_report_field_uint32(struct dm_report *rh, struct dm_report_field *field,
- const uint32_t *data);
-int dm_report_field_int(struct dm_report *rh, struct dm_report_field *field,
- const int *data);
-int dm_report_field_uint64(struct dm_report *rh, struct dm_report_field *field,
- const uint64_t *data);
-int dm_report_field_percent(struct dm_report *rh, struct dm_report_field *field,
- const dm_percent_t *data);
-
-/*
- * For custom fields, allocate the data in 'mem' and use
- * dm_report_field_set_value().
- * 'sortvalue' may be NULL if it matches 'value'
- */
-void dm_report_field_set_value(struct dm_report_field *field, const void *value,
- const void *sortvalue);
-
-/*
- * Report group support.
- */
-struct dm_report_group;
-
-typedef enum {
- DM_REPORT_GROUP_SINGLE,
- DM_REPORT_GROUP_BASIC,
- DM_REPORT_GROUP_JSON
-} dm_report_group_type_t;
-
-struct dm_report_group *dm_report_group_create(dm_report_group_type_t type, void *data);
-int dm_report_group_push(struct dm_report_group *group, struct dm_report *report, void *data);
-int dm_report_group_pop(struct dm_report_group *group);
-int dm_report_group_output_and_pop_all(struct dm_report_group *group);
-int dm_report_group_destroy(struct dm_report_group *group);
-
-/*
- * Stats counter access methods
- *
- * Each method returns the corresponding stats counter value from 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.
- *
- * Two methods are provided to access counter values: a named function
- * for each available counter field and a single function that accepts
- * an enum value specifying the required field. New code is encouraged
- * to use the enum based interface as calls to the named functions are
- * implemented using the enum method internally.
- *
- * 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
-#define DM_STATS_AREA_CURRENT UINT64_MAX
-
-typedef enum {
- DM_STATS_READS_COUNT,
- DM_STATS_READS_MERGED_COUNT,
- DM_STATS_READ_SECTORS_COUNT,
- DM_STATS_READ_NSECS,
- DM_STATS_WRITES_COUNT,
- DM_STATS_WRITES_MERGED_COUNT,
- DM_STATS_WRITE_SECTORS_COUNT,
- DM_STATS_WRITE_NSECS,
- DM_STATS_IO_IN_PROGRESS_COUNT,
- DM_STATS_IO_NSECS,
- DM_STATS_WEIGHTED_IO_NSECS,
- DM_STATS_TOTAL_READ_NSECS,
- DM_STATS_TOTAL_WRITE_NSECS,
- DM_STATS_NR_COUNTERS
-} dm_stats_counter_t;
-
-uint64_t dm_stats_get_counter(const struct dm_stats *dms,
- dm_stats_counter_t counter,
- uint64_t region_id, uint64_t area_id);
-
-uint64_t dm_stats_get_reads(const struct dm_stats *dms,
- uint64_t region_id, uint64_t area_id);
-
-uint64_t dm_stats_get_reads_merged(const struct dm_stats *dms,
- uint64_t region_id, uint64_t area_id);
-
-uint64_t dm_stats_get_read_sectors(const struct dm_stats *dms,
- uint64_t region_id, uint64_t area_id);
-
-uint64_t dm_stats_get_read_nsecs(const struct dm_stats *dms,
- uint64_t region_id, uint64_t area_id);
-
-uint64_t dm_stats_get_writes(const struct dm_stats *dms,
- uint64_t region_id, uint64_t area_id);
-
-uint64_t dm_stats_get_writes_merged(const struct dm_stats *dms,
- uint64_t region_id, uint64_t area_id);
-
-uint64_t dm_stats_get_write_sectors(const struct dm_stats *dms,
- uint64_t region_id, uint64_t area_id);
-
-uint64_t dm_stats_get_write_nsecs(const struct dm_stats *dms,
- uint64_t region_id, uint64_t area_id);
-
-uint64_t dm_stats_get_io_in_progress(const struct dm_stats *dms,
- uint64_t region_id, uint64_t area_id);
-
-uint64_t dm_stats_get_io_nsecs(const struct dm_stats *dms,
- uint64_t region_id, uint64_t area_id);
-
-uint64_t dm_stats_get_weighted_io_nsecs(const struct dm_stats *dms,
- uint64_t region_id, uint64_t area_id);
-
-uint64_t dm_stats_get_total_read_nsecs(const struct dm_stats *dms,
- uint64_t region_id, uint64_t area_id);
-
-uint64_t dm_stats_get_total_write_nsecs(const 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
- */
-
-typedef enum {
- DM_STATS_RD_MERGES_PER_SEC,
- DM_STATS_WR_MERGES_PER_SEC,
- DM_STATS_READS_PER_SEC,
- DM_STATS_WRITES_PER_SEC,
- DM_STATS_READ_SECTORS_PER_SEC,
- DM_STATS_WRITE_SECTORS_PER_SEC,
- DM_STATS_AVERAGE_REQUEST_SIZE,
- DM_STATS_AVERAGE_QUEUE_SIZE,
- DM_STATS_AVERAGE_WAIT_TIME,
- DM_STATS_AVERAGE_RD_WAIT_TIME,
- DM_STATS_AVERAGE_WR_WAIT_TIME,
- DM_STATS_SERVICE_TIME,
- DM_STATS_THROUGHPUT,
- DM_STATS_UTILIZATION,
- DM_STATS_NR_METRICS
-} dm_stats_metric_t;
-
-int dm_stats_get_metric(const struct dm_stats *dms, int metric,
- uint64_t region_id, uint64_t area_id, double *value);
-
-int dm_stats_get_rd_merges_per_sec(const struct dm_stats *dms, double *rrqm,
- uint64_t region_id, uint64_t area_id);
-
-int dm_stats_get_wr_merges_per_sec(const struct dm_stats *dms, double *rrqm,
- uint64_t region_id, uint64_t area_id);
-
-int dm_stats_get_reads_per_sec(const struct dm_stats *dms, double *rd_s,
- uint64_t region_id, uint64_t area_id);
-
-int dm_stats_get_writes_per_sec(const struct dm_stats *dms, double *wr_s,
- uint64_t region_id, uint64_t area_id);
-
-int dm_stats_get_read_sectors_per_sec(const struct dm_stats *dms,
- double *rsec_s, uint64_t region_id,
- uint64_t area_id);
-
-int dm_stats_get_write_sectors_per_sec(const struct dm_stats *dms,
- double *wr_s, uint64_t region_id,
- uint64_t area_id);
-
-int dm_stats_get_average_request_size(const struct dm_stats *dms,
- double *arqsz, uint64_t region_id,
- uint64_t area_id);
-
-int dm_stats_get_service_time(const struct dm_stats *dms, double *svctm,
- uint64_t region_id, uint64_t area_id);
-
-int dm_stats_get_average_queue_size(const struct dm_stats *dms, double *qusz,
- uint64_t region_id, uint64_t area_id);
-
-int dm_stats_get_average_wait_time(const struct dm_stats *dms, double *await,
- uint64_t region_id, uint64_t area_id);
-
-int dm_stats_get_average_rd_wait_time(const struct dm_stats *dms,
- double *await, uint64_t region_id,
- uint64_t area_id);
-
-int dm_stats_get_average_wr_wait_time(const struct dm_stats *dms,
- double *await, uint64_t region_id,
- uint64_t area_id);
-
-int dm_stats_get_throughput(const struct dm_stats *dms, double *tput,
- uint64_t region_id, uint64_t area_id);
-
-int dm_stats_get_utilization(const struct dm_stats *dms, dm_percent_t *util,
- uint64_t region_id, uint64_t area_id);
-
-/*
- * Statistics histogram access methods.
- *
- * Methods to access latency histograms for regions that have them
- * enabled. Each histogram contains a configurable number of bins
- * spanning a user defined latency interval.
- *
- * The bin count, upper and lower bin bounds, and bin values are
- * made available via the following area methods.
- *
- * Methods to obtain a simple string representation of the histogram
- * and its bounds are also provided.
- */
-
-/*
- * Retrieve a pointer to the histogram associated with the specified
- * area. If the area does not have a histogram configured this function
- * returns NULL.
- *
- * The pointer does not need to be freed explicitly by the caller: it
- * will become invalid following a subsequent dm_stats_list(),
- * dm_stats_populate() or dm_stats_destroy() of the corresponding
- * dm_stats handle.
- *
- * If region_id or area_id is one of the special values
- * DM_STATS_REGION_CURRENT or DM_STATS_AREA_CURRENT the current cursor
- * value is used to select the region or area.
- */
-struct dm_histogram *dm_stats_get_histogram(const struct dm_stats *dms,
- uint64_t region_id,
- uint64_t area_id);
-
-/*
- * Return the number of bins in the specified histogram handle.
- */
-int dm_histogram_get_nr_bins(const struct dm_histogram *dmh);
-
-/*
- * Get the lower bound of the specified bin of the histogram for the
- * area specified by region_id and area_id. The value is returned in
- * nanoseconds.
- */
-uint64_t dm_histogram_get_bin_lower(const struct dm_histogram *dmh, int bin);
-
-/*
- * Get the upper bound of the specified bin of the histogram for the
- * area specified by region_id and area_id. The value is returned in
- * nanoseconds.
- */
-uint64_t dm_histogram_get_bin_upper(const struct dm_histogram *dmh, int bin);
-
-/*
- * Get the width of the specified bin of the histogram for the area
- * specified by region_id and area_id. The width is equal to the bin
- * upper bound minus the lower bound and yields the range of latency
- * values covered by this bin. The value is returned in nanoseconds.
- */
-uint64_t dm_histogram_get_bin_width(const struct dm_histogram *dmh, int bin);
-
-/*
- * Get the value of the specified bin of the histogram for the area
- * specified by region_id and area_id.
- */
-uint64_t dm_histogram_get_bin_count(const struct dm_histogram *dmh, int bin);
-
-/*
- * Get the percentage (relative frequency) of the specified bin of the
- * histogram for the area specified by region_id and area_id.
- */
-dm_percent_t dm_histogram_get_bin_percent(const struct dm_histogram *dmh,
- int bin);
-
-/*
- * Return the total observations (sum of bin counts) for the histogram
- * of the area specified by region_id and area_id.
- */
-uint64_t dm_histogram_get_sum(const struct dm_histogram *dmh);
-
-/*
- * Histogram formatting flags.
- */
-#define DM_HISTOGRAM_SUFFIX 0x1
-#define DM_HISTOGRAM_VALUES 0x2
-#define DM_HISTOGRAM_PERCENT 0X4
-#define DM_HISTOGRAM_BOUNDS_LOWER 0x10
-#define DM_HISTOGRAM_BOUNDS_UPPER 0x20
-#define DM_HISTOGRAM_BOUNDS_RANGE 0x30
-
-/*
- * Return a string representation of the supplied histogram's values and
- * bin boundaries.
- *
- * The bin argument selects the bin to format. If this argument is less
- * than zero all bins will be included in the resulting string.
- *
- * width specifies a minimum width for the field in characters; if it is
- * zero the width will be determined automatically based on the options
- * selected for formatting. A value less than zero disables field width
- * control: bin boundaries and values will be output with a minimum
- * amount of whitespace.
- *
- * flags is a collection of flag arguments that control the string format:
- *
- * DM_HISTOGRAM_VALUES - Include bin values in the string.
- * DM_HISTOGRAM_SUFFIX - Include time unit suffixes when printing bounds.
- * DM_HISTOGRAM_PERCENT - Format bin values as a percentage.
- *
- * DM_HISTOGRAM_BOUNDS_LOWER - Include the lower bound of each bin.
- * DM_HISTOGRAM_BOUNDS_UPPER - Include the upper bound of each bin.
- * DM_HISTOGRAM_BOUNDS_RANGE - Show the span of each bin as "lo-up".
- *
- * The returned pointer does not need to be freed explicitly by the
- * caller: it will become invalid following a subsequent
- * dm_stats_list(), dm_stats_populate() or dm_stats_destroy() of the
- * corresponding dm_stats handle.
- */
-const char *dm_histogram_to_string(const struct dm_histogram *dmh, int bin,
- int width, int flags);
-
-/*************************
- * config file parse/print
- *************************/
-typedef enum {
- DM_CFG_INT,
- DM_CFG_FLOAT,
- DM_CFG_STRING,
- DM_CFG_EMPTY_ARRAY
-} dm_config_value_type_t;
-
-struct dm_config_value {
- dm_config_value_type_t type;
-
- union {
- int64_t i;
- float f;
- double d; /* Unused. */
- const char *str;
- } v;
-
- struct dm_config_value *next; /* For arrays */
- uint32_t format_flags;
-};
-
-struct dm_config_node {
- const char *key;
- struct dm_config_node *parent, *sib, *child;
- struct dm_config_value *v;
- int id;
-};
-
-struct dm_config_tree {
- struct dm_config_node *root;
- struct dm_config_tree *cascade;
- struct dm_pool *mem;
- void *custom;
-};
-
-struct dm_config_tree *dm_config_create(void);
-struct dm_config_tree *dm_config_from_string(const char *config_settings);
-int dm_config_parse(struct dm_config_tree *cft, const char *start, const char *end);
-int dm_config_parse_without_dup_node_check(struct dm_config_tree *cft, const char *start, const char *end);
-
-void *dm_config_get_custom(struct dm_config_tree *cft);
-void dm_config_set_custom(struct dm_config_tree *cft, void *custom);
-
-/*
- * When searching, first_cft is checked before second_cft.
- */
-struct dm_config_tree *dm_config_insert_cascaded_tree(struct dm_config_tree *first_cft, struct dm_config_tree *second_cft);
-
-/*
- * If there's a cascaded dm_config_tree, remove the top layer
- * and return the layer below. Otherwise return NULL.
- */
-struct dm_config_tree *dm_config_remove_cascaded_tree(struct dm_config_tree *cft);
-
-/*
- * Create a new, uncascaded config tree equivalent to the input cascade.
- */
-struct dm_config_tree *dm_config_flatten(struct dm_config_tree *cft);
-
-void dm_config_destroy(struct dm_config_tree *cft);
-
-/* Simple output line by line. */
-typedef int (*dm_putline_fn)(const char *line, void *baton);
-/* More advaced output with config node reference. */
-typedef int (*dm_config_node_out_fn)(const struct dm_config_node *cn, const char *line, void *baton);
-
-/*
- * Specification for advanced config node output.
- */
-struct dm_config_node_out_spec {
- dm_config_node_out_fn prefix_fn; /* called before processing config node lines */
- dm_config_node_out_fn line_fn; /* called for each config node line */
- dm_config_node_out_fn suffix_fn; /* called after processing config node lines */
-};
-
-/* Write the node and any subsequent siblings it has. */
-int dm_config_write_node(const struct dm_config_node *cn, dm_putline_fn putline, void *baton);
-int dm_config_write_node_out(const struct dm_config_node *cn, const struct dm_config_node_out_spec *out_spec, void *baton);
-
-/* Write given node only without subsequent siblings. */
-int dm_config_write_one_node(const struct dm_config_node *cn, dm_putline_fn putline, void *baton);
-int dm_config_write_one_node_out(const struct dm_config_node *cn, const struct dm_config_node_out_spec *out_spec, void *baton);
-
-struct dm_config_node *dm_config_find_node(const struct dm_config_node *cn, const char *path);
-int dm_config_has_node(const struct dm_config_node *cn, const char *path);
-int dm_config_remove_node(struct dm_config_node *parent, struct dm_config_node *remove);
-const char *dm_config_find_str(const struct dm_config_node *cn, const char *path, const char *fail);
-const char *dm_config_find_str_allow_empty(const struct dm_config_node *cn, const char *path, const char *fail);
-int dm_config_find_int(const struct dm_config_node *cn, const char *path, int fail);
-int64_t dm_config_find_int64(const struct dm_config_node *cn, const char *path, int64_t fail);
-float dm_config_find_float(const struct dm_config_node *cn, const char *path, float fail);
-
-const struct dm_config_node *dm_config_tree_find_node(const struct dm_config_tree *cft, const char *path);
-const char *dm_config_tree_find_str(const struct dm_config_tree *cft, const char *path, const char *fail);
-const char *dm_config_tree_find_str_allow_empty(const struct dm_config_tree *cft, const char *path, const char *fail);
-int dm_config_tree_find_int(const struct dm_config_tree *cft, const char *path, int fail);
-int64_t dm_config_tree_find_int64(const struct dm_config_tree *cft, const char *path, int64_t fail);
-float dm_config_tree_find_float(const struct dm_config_tree *cft, const char *path, float fail);
-int dm_config_tree_find_bool(const struct dm_config_tree *cft, const char *path, int fail);
-
-/*
- * Understands (0, ~0), (y, n), (yes, no), (on,
- * off), (true, false).
- */
-int dm_config_find_bool(const struct dm_config_node *cn, const char *path, int fail);
-int dm_config_value_is_bool(const struct dm_config_value *v);
-
-int dm_config_get_uint32(const struct dm_config_node *cn, const char *path, uint32_t *result);
-int dm_config_get_uint64(const struct dm_config_node *cn, const char *path, uint64_t *result);
-int dm_config_get_str(const struct dm_config_node *cn, const char *path, const char **result);
-int dm_config_get_list(const struct dm_config_node *cn, const char *path, const struct dm_config_value **result);
-int dm_config_get_section(const struct dm_config_node *cn, const char *path, const struct dm_config_node **result);
-
-unsigned dm_config_maybe_section(const char *str, unsigned len);
-
-const char *dm_config_parent_name(const struct dm_config_node *n);
-
-struct dm_config_node *dm_config_clone_node_with_mem(struct dm_pool *mem, const struct dm_config_node *node, int siblings);
-struct dm_config_node *dm_config_create_node(struct dm_config_tree *cft, const char *key);
-struct dm_config_value *dm_config_create_value(struct dm_config_tree *cft);
-struct dm_config_node *dm_config_clone_node(struct dm_config_tree *cft, const struct dm_config_node *cn, int siblings);
-
-/*
- * Common formatting flags applicable to all config node types (lower 16 bits).
- */
-#define DM_CONFIG_VALUE_FMT_COMMON_ARRAY 0x00000001 /* value is array */
-#define DM_CONFIG_VALUE_FMT_COMMON_EXTRA_SPACES 0x00000002 /* add spaces in "key = value" pairs in constrast to "key=value" for better readability */
-
-/*
- * Type-related config node formatting flags (higher 16 bits).
- */
-/* int-related formatting flags */
-#define DM_CONFIG_VALUE_FMT_INT_OCTAL 0x00010000 /* print number in octal form */
-
-/* string-related formatting flags */
-#define DM_CONFIG_VALUE_FMT_STRING_NO_QUOTES 0x00010000 /* do not print quotes around string value */
-
-void dm_config_value_set_format_flags(struct dm_config_value *cv, uint32_t format_flags);
-uint32_t dm_config_value_get_format_flags(struct dm_config_value *cv);
-
-struct dm_pool *dm_config_memory(struct dm_config_tree *cft);
-
-/* Udev device directory. */
-#define DM_UDEV_DEV_DIR "/dev/"
-
-/* Cookie prefixes.
- *
- * The cookie value consists of a prefix (16 bits) and a base (16 bits).
- * We can use the prefix to store the flags. These flags are sent to
- * kernel within given dm task. When returned back to userspace in
- * DM_COOKIE udev environment variable, we can control several aspects
- * of udev rules we use by decoding the cookie prefix. When doing the
- * notification, we replace the cookie prefix with DM_COOKIE_MAGIC,
- * so we notify the right semaphore.
- *
- * It is still possible to use cookies for passing the flags to udev
- * rules even when udev_sync is disabled. The base part of the cookie
- * will be zero (there's no notification semaphore) and prefix will be
- * set then. However, having udev_sync enabled is highly recommended.
- */
-#define DM_COOKIE_MAGIC 0x0D4D
-#define DM_UDEV_FLAGS_MASK 0xFFFF0000
-#define DM_UDEV_FLAGS_SHIFT 16
-
-/*
- * DM_UDEV_DISABLE_DM_RULES_FLAG is set in case we need to disable
- * basic device-mapper udev rules that create symlinks in /dev/<DM_DIR>
- * directory. However, we can't reliably prevent creating default
- * nodes by udev (commonly /dev/dm-X, where X is a number).
- */
-#define DM_UDEV_DISABLE_DM_RULES_FLAG 0x0001
-/*
- * DM_UDEV_DISABLE_SUBSYTEM_RULES_FLAG is set in case we need to disable
- * subsystem udev rules, but still we need the general DM udev rules to
- * be applied (to create the nodes and symlinks under /dev and /dev/disk).
- */
-#define DM_UDEV_DISABLE_SUBSYSTEM_RULES_FLAG 0x0002
-/*
- * DM_UDEV_DISABLE_DISK_RULES_FLAG is set in case we need to disable
- * general DM rules that set symlinks in /dev/disk directory.
- */
-#define DM_UDEV_DISABLE_DISK_RULES_FLAG 0x0004
-/*
- * DM_UDEV_DISABLE_OTHER_RULES_FLAG is set in case we need to disable
- * all the other rules that are not general device-mapper nor subsystem
- * related (the rules belong to other software or packages). All foreign
- * rules should check this flag directly and they should ignore further
- * rule processing for such event.
- */
-#define DM_UDEV_DISABLE_OTHER_RULES_FLAG 0x0008
-/*
- * DM_UDEV_LOW_PRIORITY_FLAG is set in case we need to instruct the
- * udev rules to give low priority to the device that is currently
- * processed. For example, this provides a way to select which symlinks
- * could be overwritten by high priority ones if their names are equal.
- * Common situation is a name based on FS UUID while using origin and
- * snapshot devices.
- */
-#define DM_UDEV_LOW_PRIORITY_FLAG 0x0010
-/*
- * DM_UDEV_DISABLE_LIBRARY_FALLBACK is set in case we need to disable
- * libdevmapper's node management. We will rely on udev completely
- * and there will be no fallback action provided by libdevmapper if
- * udev does something improperly. Using the library fallback code has
- * a consequence that you need to take into account: any device node
- * or symlink created without udev is not recorded in udev database
- * which other applications may read to get complete list of devices.
- * For this reason, use of DM_UDEV_DISABLE_LIBRARY_FALLBACK is
- * recommended on systems where udev is used. Keep library fallback
- * enabled just for exceptional cases where you need to debug udev-related
- * problems. If you hit such problems, please contact us through upstream
- * LVM2 development mailing list (see also README file). This flag is
- * currently not set by default in libdevmapper so you need to set it
- * explicitly if you're sure that udev is behaving correctly on your
- * setups.
- */
-#define DM_UDEV_DISABLE_LIBRARY_FALLBACK 0x0020
-/*
- * DM_UDEV_PRIMARY_SOURCE_FLAG is automatically appended by
- * libdevmapper for all ioctls generating udev uevents. Once used in
- * udev rules, we know if this is a real "primary sourced" event or not.
- * We need to distinguish real events originated in libdevmapper from
- * any spurious events to gather all missing information (e.g. events
- * generated as a result of "udevadm trigger" command or as a result
- * of the "watch" udev rule).
- */
-#define DM_UDEV_PRIMARY_SOURCE_FLAG 0x0040
-
-/*
- * Udev flags reserved for use by any device-mapper subsystem.
- */
-#define DM_SUBSYSTEM_UDEV_FLAG0 0x0100
-#define DM_SUBSYSTEM_UDEV_FLAG1 0x0200
-#define DM_SUBSYSTEM_UDEV_FLAG2 0x0400
-#define DM_SUBSYSTEM_UDEV_FLAG3 0x0800
-#define DM_SUBSYSTEM_UDEV_FLAG4 0x1000
-#define DM_SUBSYSTEM_UDEV_FLAG5 0x2000
-#define DM_SUBSYSTEM_UDEV_FLAG6 0x4000
-#define DM_SUBSYSTEM_UDEV_FLAG7 0x8000
-
-int dm_cookie_supported(void);
-
-/*
- * Udev synchronisation functions.
- */
-void dm_udev_set_sync_support(int sync_with_udev);
-int dm_udev_get_sync_support(void);
-void dm_udev_set_checking(int checking);
-int dm_udev_get_checking(void);
-
-/*
- * Default value to get new auto generated cookie created
- */
-#define DM_COOKIE_AUTO_CREATE 0
-int dm_udev_create_cookie(uint32_t *cookie);
-int dm_udev_complete(uint32_t cookie);
-int dm_udev_wait(uint32_t cookie);
-
-/*
- * dm_dev_wait_immediate
- * If *ready is 1 on return, the wait is complete.
- * If *ready is 0 on return, the wait is incomplete and either
- * this function or dm_udev_wait() must be called again.
- * Returns 0 on error, when neither function should be called again.
- */
-int dm_udev_wait_immediate(uint32_t cookie, int *ready);
-
-#define DM_DEV_DIR_UMASK 0022
-#define DM_CONTROL_NODE_UMASK 0177
-
-#ifdef __cplusplus
-}
-#endif
-#endif /* LIB_DEVICE_MAPPER_H */
diff --git a/device_mapper/libdm-common.h b/device_mapper/libdm-common.h
index 010d876..cf99265 100644
--- a/device_mapper/libdm-common.h
+++ b/device_mapper/libdm-common.h
@@ -16,7 +16,7 @@
#ifndef LIB_DMCOMMON_H
#define LIB_DMCOMMON_H
-#include "libdevmapper.h"
+#include "device_mapper/all.h"
#define DM_DEFAULT_NAME_MANGLING_MODE_ENV_VAR_NAME "DM_DEFAULT_NAME_MANGLING_MODE"
diff --git a/device_mapper/misc/dm-logging.h b/device_mapper/misc/dm-logging.h
index e355bc5..4db38bd 100644
--- a/device_mapper/misc/dm-logging.h
+++ b/device_mapper/misc/dm-logging.h
@@ -16,7 +16,7 @@
#ifndef _DM_LOGGING_H
#define _DM_LOGGING_H
-#include "device_mapper/libdevmapper.h"
+#include "device_mapper/all.h"
extern dm_log_with_errno_fn dm_log_with_errno;
diff --git a/device_mapper/misc/dmlib.h b/device_mapper/misc/dmlib.h
index 88b6ac3..fe64bd7 100644
--- a/device_mapper/misc/dmlib.h
+++ b/device_mapper/misc/dmlib.h
@@ -26,7 +26,7 @@
#define _REENTRANT
#define _GNU_SOURCE
-#include "device_mapper/libdevmapper.h"
+#include "device_mapper/all.h"
#include "lib/misc/util.h"
#include "dm-logging.h"
diff --git a/device_mapper/vdo/status.c b/device_mapper/vdo/status.c
index 4bd15e4..7a26e1a 100644
--- a/device_mapper/vdo/status.c
+++ b/device_mapper/vdo/status.c
@@ -1,7 +1,7 @@
#include "target.h"
// For DM_ARRAY_SIZE!
-#include "device_mapper/libdevmapper.h"
+#include "device_mapper/all.h"
#include <ctype.h>
#include <stdlib.h>
diff --git a/lib/config/config.h b/lib/config/config.h
index 146aae1..4c8ddac 100644
--- a/lib/config/config.h
+++ b/lib/config/config.h
@@ -16,7 +16,7 @@
#ifndef _LVM_CONFIG_H
#define _LVM_CONFIG_H
-#include "device_mapper/libdevmapper.h"
+#include "device_mapper/all.h"
#include "lib/device/device.h"
/* 16 bits: 3 bits for major, 4 bits for minor, 9 bits for patchlevel */
diff --git a/lib/device/bcache.h b/lib/device/bcache.h
index 8f328c7..cb36c30 100644
--- a/lib/device/bcache.h
+++ b/lib/device/bcache.h
@@ -15,7 +15,7 @@
#ifndef BCACHE_H
#define BCACHE_H
-#include "device_mapper/libdevmapper.h"
+#include "device_mapper/all.h"
#include <linux/fs.h>
#include <stdint.h>
diff --git a/lib/metadata/pv.h b/lib/metadata/pv.h
index d5d91ce..61aa54e 100644
--- a/lib/metadata/pv.h
+++ b/lib/metadata/pv.h
@@ -16,7 +16,7 @@
#define _LVM_PV_H
#include "lib/uuid/uuid.h"
-#include "device_mapper/libdevmapper.h"
+#include "device_mapper/all.h"
struct device;
struct format_type;
diff --git a/lib/metadata/vg.h b/lib/metadata/vg.h
index 6208159..9921b91 100644
--- a/lib/metadata/vg.h
+++ b/lib/metadata/vg.h
@@ -16,7 +16,7 @@
#define _LVM_VG_H
#include "lib/uuid/uuid.h"
-#include "device_mapper/libdevmapper.h"
+#include "device_mapper/all.h"
struct cmd_context;
struct format_instance;
diff --git a/lib/misc/lib.h b/lib/misc/lib.h
index 3ae3aac..0d61f34 100644
--- a/lib/misc/lib.h
+++ b/lib/misc/lib.h
@@ -79,7 +79,7 @@
#include "lib/misc/intl.h"
-#include "device_mapper/libdevmapper.h"
+#include "device_mapper/all.h"
#include "lib/misc/util.h"
#ifdef DM
diff --git a/lib/report/properties.h b/lib/report/properties.h
index 38b6111..3d93b08 100644
--- a/lib/report/properties.h
+++ b/lib/report/properties.h
@@ -14,7 +14,7 @@
#ifndef _LVM_PROPERTIES_H
#define _LVM_PROPERTIES_H
-#include "device_mapper/libdevmapper.h"
+#include "device_mapper/all.h"
#include "lib/metadata/metadata.h"
#include "lib/report/report.h"
#include "lib/properties/prop_common.h"
diff --git a/scripts/lvm2_activation_generator_systemd_red_hat.c b/scripts/lvm2_activation_generator_systemd_red_hat.c
index 9ba1996..7791e64 100644
--- a/scripts/lvm2_activation_generator_systemd_red_hat.c
+++ b/scripts/lvm2_activation_generator_systemd_red_hat.c
@@ -27,7 +27,7 @@
#include <unistd.h>
#include "configure.h"
-#include "device_mapper/libdevmapper.h"
+#include "device_mapper/all.h"
//----------------------------------------------------------------
diff --git a/test/unit/activation-generator_t.c b/test/unit/activation-generator_t.c
index 56871a7..9f6b661 100644
--- a/test/unit/activation-generator_t.c
+++ b/test/unit/activation-generator_t.c
@@ -16,7 +16,7 @@
#include "framework.h"
#include "units.h"
-#include "device_mapper/libdevmapper.h"
+#include "device_mapper/all.h"
//----------------------------------------------------------------
diff --git a/test/unit/bitset_t.c b/test/unit/bitset_t.c
index 9b18fcb..12b2a20 100644
--- a/test/unit/bitset_t.c
+++ b/test/unit/bitset_t.c
@@ -13,7 +13,7 @@
*/
#include "units.h"
-#include "device_mapper/libdevmapper.h"
+#include "device_mapper/all.h"
enum {
NR_BITS = 137
diff --git a/test/unit/config_t.c b/test/unit/config_t.c
index 21af551..50d388f 100644
--- a/test/unit/config_t.c
+++ b/test/unit/config_t.c
@@ -13,7 +13,7 @@
*/
#include "units.h"
-#include "device_mapper/libdevmapper.h"
+#include "device_mapper/all.h"
static void *_mem_init(void)
{
diff --git a/test/unit/dmlist_t.c b/test/unit/dmlist_t.c
index 8a9948f..0e33385 100644
--- a/test/unit/dmlist_t.c
+++ b/test/unit/dmlist_t.c
@@ -13,7 +13,7 @@
*/
#include "units.h"
-#include "device_mapper/libdevmapper.h"
+#include "device_mapper/all.h"
static void test_dmlist_splice(void *fixture)
{
diff --git a/test/unit/dmstatus_t.c b/test/unit/dmstatus_t.c
index f50dd75..ac2015e 100644
--- a/test/unit/dmstatus_t.c
+++ b/test/unit/dmstatus_t.c
@@ -13,7 +13,7 @@
*/
#include "units.h"
-#include "device_mapper/libdevmapper.h"
+#include "device_mapper/all.h"
static void *_mem_init(void)
{
diff --git a/test/unit/framework.h b/test/unit/framework.h
index 0a8a5f2..0c45596 100644
--- a/test/unit/framework.h
+++ b/test/unit/framework.h
@@ -1,7 +1,7 @@
#ifndef TEST_UNIT_FRAMEWORK_H
#define TEST_UNIT_FRAMEWORK_H
-#include "device_mapper/libdevmapper.h"
+#include "device_mapper/all.h"
#include <stdbool.h>
#include <stdint.h>
diff --git a/test/unit/matcher_t.c b/test/unit/matcher_t.c
index 296c78a..89b2988 100644
--- a/test/unit/matcher_t.c
+++ b/test/unit/matcher_t.c
@@ -14,7 +14,7 @@
*/
#include "units.h"
-#include "device_mapper/libdevmapper.h"
+#include "device_mapper/all.h"
#include "matcher_data.h"
diff --git a/test/unit/percent_t.c b/test/unit/percent_t.c
index 4341480..9b349a9 100644
--- a/test/unit/percent_t.c
+++ b/test/unit/percent_t.c
@@ -13,7 +13,7 @@
*/
#include "units.h"
-#include "device_mapper/libdevmapper.h"
+#include "device_mapper/all.h"
#include <stdio.h>
#include <string.h>
diff --git a/test/unit/string_t.c b/test/unit/string_t.c
index 3557247..82c6448 100644
--- a/test/unit/string_t.c
+++ b/test/unit/string_t.c
@@ -13,7 +13,7 @@
*/
#include "units.h"
-#include "device_mapper/libdevmapper.h"
+#include "device_mapper/all.h"
#include <stdio.h>
#include <string.h>
diff --git a/tools/tool.h b/tools/tool.h
index 51d530c..a4cef3d 100644
--- a/tools/tool.h
+++ b/tools/tool.h
@@ -24,7 +24,7 @@
#include <unistd.h>
-#include "device_mapper/libdevmapper.h"
+#include "device_mapper/all.h"
#include "lib/misc/util.h"
#endif /* _LVM_TOOL_H */
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=c78239d8605f333915543…
Commit: c78239d8605f333915543c0e0c3ccf4f4ef5ee8f
Parent: 286c1ba3369d1901480425e305e313e8ea365da4
Author: Joe Thornber <ejt(a)redhat.com>
AuthorDate: Fri Jun 8 13:01:41 2018 +0100
Committer: Joe Thornber <ejt(a)redhat.com>
CommitterDate: Fri Jun 8 13:01:41 2018 +0100
libdm: Stop libdm/misc/dmlib.h from including lib/misc/lib.h
---
libdm/misc/dmlib.h | 67 +++++++++++++++++++++++++++++++++++++++++++++++++--
1 files changed, 64 insertions(+), 3 deletions(-)
diff --git a/libdm/misc/dmlib.h b/libdm/misc/dmlib.h
index bc14ece..4c6bcfc 100644
--- a/libdm/misc/dmlib.h
+++ b/libdm/misc/dmlib.h
@@ -14,13 +14,74 @@
*/
/*
- * This file must be included first by every device-mapper library source file.
+ * This file must be included first by every library source file.
*/
#ifndef _DM_LIB_H
#define _DM_LIB_H
-#define DM
+#include "configure.h"
-#include "lib/misc/lib.h"
+#define _REENTRANT
+#define _GNU_SOURCE
+
+/*
+ * Symbol export control macros
+ *
+ * DM_EXPORT_SYMBOL(func,ver)
+ * DM_EXPORT_SYMBOL_BASE(func,ver)
+ *
+ * For functions that have multiple implementations these macros control
+ * symbol export and versioning.
+ *
+ * Function definitions that exist in only one version never need to use
+ * these macros.
+ *
+ * Backwards compatible implementations must include a version tag of
+ * the form "_v1_02_104" as a suffix to the function name and use the
+ * macro DM_EXPORT_SYMBOL to export the function and bind it to the
+ * specified version string.
+ *
+ * Since versioning is only available when compiling with GCC the entire
+ * compatibility version should be enclosed in '#if defined(__GNUC__)',
+ * for example:
+ *
+ * int dm_foo(int bar)
+ * {
+ * return bar;
+ * }
+ *
+ * #if defined(__GNUC__)
+ * // Backward compatible dm_foo() version 1.02.104
+ * int dm_foo_v1_02_104(void);
+ * int dm_foo_v1_02_104(void)
+ * {
+ * return 0;
+ * }
+ * DM_EXPORT_SYMBOL(dm_foo,1_02_104)
+ * #endif
+ *
+ * A prototype for the compatibility version is required as these
+ * functions must not be declared static.
+ *
+ * The DM_EXPORT_SYMBOL_BASE macro is only used to export the base
+ * versions of library symbols prior to the introduction of symbol
+ * versioning: it must never be used for new symbols.
+ */
+#if defined(__GNUC__)
+#define DM_EXPORT_SYMBOL(func, ver) \
+ __asm__(".symver " #func "_v" #ver ", " #func "@DM_" #ver )
+#define DM_EXPORT_SYMBOL_BASE(func) \
+ __asm__(".symver " #func "_base, " #func "@Base" )
+#else
+#define DM_EXPORT_SYMBOL(func, ver)
+#define DM_EXPORT_SYMBOL_BASE(func)
+#endif
+
+#include "lib/misc/util.h"
+
+#include "libdm/libdevmapper.h"
+#include "libdm/misc/dm-logging.h"
+
+#include <unistd.h>
#endif
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=88ae928ca3f950fa0ecb8…
Commit: 88ae928ca3f950fa0ecb8b8e0aff118b7360a9d7
Parent: 9573ff3a3bbd61fb178ea73e24c689822b9aca85
Author: Joe Thornber <ejt(a)redhat.com>
AuthorDate: Fri Jun 8 11:24:18 2018 +0100
Committer: Joe Thornber <ejt(a)redhat.com>
CommitterDate: Fri Jun 8 11:24:18 2018 +0100
base: Move list to base/data-struct
---
base/Makefile | 1 +
base/data-struct/list.c | 170 +++++++++++++++++++++++++++++++
base/data-struct/list.h | 209 +++++++++++++++++++++++++++++++++++++++
device_mapper/Makefile | 1 -
device_mapper/datastruct/list.c | 168 -------------------------------
device_mapper/libdevmapper.h | 209 +--------------------------------------
6 files changed, 384 insertions(+), 374 deletions(-)
diff --git a/base/Makefile b/base/Makefile
index 27f539f..4dcb6b6 100644
--- a/base/Makefile
+++ b/base/Makefile
@@ -12,6 +12,7 @@
BASE_SOURCE=\
base/data-struct/radix-tree.c \
+ base/data-struct/list.c
BASE_DEPENDS=$(addprefix $(top_builddir)/,$(subst .c,.d,$(BASE_SOURCE)))
BASE_OBJECTS=$(addprefix $(top_builddir)/,$(subst .c,.o,$(BASE_SOURCE)))
diff --git a/base/data-struct/list.c b/base/data-struct/list.c
new file mode 100644
index 0000000..894e273
--- /dev/null
+++ b/base/data-struct/list.c
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "list.h"
+
+#include <assert.h>
+#include <stdlib.h>
+
+/*
+ * Initialise a list before use.
+ * The list head's next and previous pointers point back to itself.
+ */
+void dm_list_init(struct dm_list *head)
+{
+ head->n = head->p = head;
+}
+
+/*
+ * Insert an element before 'head'.
+ * If 'head' is the list head, this adds an element to the end of the list.
+ */
+void dm_list_add(struct dm_list *head, struct dm_list *elem)
+{
+ assert(head->n);
+
+ elem->n = head;
+ elem->p = head->p;
+
+ head->p->n = elem;
+ head->p = elem;
+}
+
+/*
+ * Insert an element after 'head'.
+ * If 'head' is the list head, this adds an element to the front of the list.
+ */
+void dm_list_add_h(struct dm_list *head, struct dm_list *elem)
+{
+ assert(head->n);
+
+ elem->n = head->n;
+ elem->p = head;
+
+ head->n->p = elem;
+ head->n = elem;
+}
+
+/*
+ * Delete an element from its list.
+ * Note that this doesn't change the element itself - it may still be safe
+ * to follow its pointers.
+ */
+void dm_list_del(struct dm_list *elem)
+{
+ elem->n->p = elem->p;
+ elem->p->n = elem->n;
+}
+
+/*
+ * Remove an element from existing list and insert before 'head'.
+ */
+void dm_list_move(struct dm_list *head, struct dm_list *elem)
+{
+ dm_list_del(elem);
+ dm_list_add(head, elem);
+}
+
+/*
+ * Is the list empty?
+ */
+int dm_list_empty(const struct dm_list *head)
+{
+ return head->n == head;
+}
+
+/*
+ * Is this the first element of the list?
+ */
+int dm_list_start(const struct dm_list *head, const struct dm_list *elem)
+{
+ return elem->p == head;
+}
+
+/*
+ * Is this the last element of the list?
+ */
+int dm_list_end(const struct dm_list *head, const struct dm_list *elem)
+{
+ return elem->n == head;
+}
+
+/*
+ * Return first element of the list or NULL if empty
+ */
+struct dm_list *dm_list_first(const struct dm_list *head)
+{
+ return (dm_list_empty(head) ? NULL : head->n);
+}
+
+/*
+ * Return last element of the list or NULL if empty
+ */
+struct dm_list *dm_list_last(const struct dm_list *head)
+{
+ return (dm_list_empty(head) ? NULL : head->p);
+}
+
+/*
+ * Return the previous element of the list, or NULL if we've reached the start.
+ */
+struct dm_list *dm_list_prev(const struct dm_list *head, const struct dm_list *elem)
+{
+ return (dm_list_start(head, elem) ? NULL : elem->p);
+}
+
+/*
+ * Return the next element of the list, or NULL if we've reached the end.
+ */
+struct dm_list *dm_list_next(const struct dm_list *head, const struct dm_list *elem)
+{
+ return (dm_list_end(head, elem) ? NULL : elem->n);
+}
+
+/*
+ * Return the number of elements in a list by walking it.
+ */
+unsigned int dm_list_size(const struct dm_list *head)
+{
+ unsigned int s = 0;
+ const struct dm_list *v;
+
+ dm_list_iterate(v, head)
+ s++;
+
+ return s;
+}
+
+/*
+ * Join two lists together.
+ * This moves all the elements of the list 'head1' to the end of the list
+ * 'head', leaving 'head1' empty.
+ */
+void dm_list_splice(struct dm_list *head, struct dm_list *head1)
+{
+ assert(head->n);
+ assert(head1->n);
+
+ if (dm_list_empty(head1))
+ return;
+
+ head1->p->n = head;
+ head1->n->p = head->p;
+
+ head->p->n = head1->n;
+ head->p = head1->p;
+
+ dm_list_init(head1);
+}
diff --git a/base/data-struct/list.h b/base/data-struct/list.h
new file mode 100644
index 0000000..1a107d1
--- /dev/null
+++ b/base/data-struct/list.h
@@ -0,0 +1,209 @@
+#ifndef BASE_DATA_STRUCT_LIST_H
+#define BASE_DATA_STRUCT_LIST_H
+
+//----------------------------------------------------------------
+
+/*
+ * A list consists of a list head plus elements.
+ * Each element has 'next' and 'previous' pointers.
+ * The list head's pointers point to the first and the last element.
+ */
+
+struct dm_list {
+ struct dm_list *n, *p;
+};
+
+/*
+ * String list.
+ */
+struct dm_str_list {
+ struct dm_list list;
+ const char *str;
+};
+
+/*
+ * Initialise a list before use.
+ * The list head's next and previous pointers point back to itself.
+ */
+#define DM_LIST_HEAD_INIT(name) { &(name), &(name) }
+#define DM_LIST_INIT(name) struct dm_list name = DM_LIST_HEAD_INIT(name)
+void dm_list_init(struct dm_list *head);
+
+/*
+ * Insert an element before 'head'.
+ * If 'head' is the list head, this adds an element to the end of the list.
+ */
+void dm_list_add(struct dm_list *head, struct dm_list *elem);
+
+/*
+ * Insert an element after 'head'.
+ * If 'head' is the list head, this adds an element to the front of the list.
+ */
+void dm_list_add_h(struct dm_list *head, struct dm_list *elem);
+
+/*
+ * Delete an element from its list.
+ * Note that this doesn't change the element itself - it may still be safe
+ * to follow its pointers.
+ */
+void dm_list_del(struct dm_list *elem);
+
+/*
+ * Remove an element from existing list and insert before 'head'.
+ */
+void dm_list_move(struct dm_list *head, struct dm_list *elem);
+
+/*
+ * Join 'head1' to the end of 'head'.
+ */
+void dm_list_splice(struct dm_list *head, struct dm_list *head1);
+
+/*
+ * Is the list empty?
+ */
+int dm_list_empty(const struct dm_list *head);
+
+/*
+ * Is this the first element of the list?
+ */
+int dm_list_start(const struct dm_list *head, const struct dm_list *elem);
+
+/*
+ * Is this the last element of the list?
+ */
+int dm_list_end(const struct dm_list *head, const struct dm_list *elem);
+
+/*
+ * Return first element of the list or NULL if empty
+ */
+struct dm_list *dm_list_first(const struct dm_list *head);
+
+/*
+ * Return last element of the list or NULL if empty
+ */
+struct dm_list *dm_list_last(const struct dm_list *head);
+
+/*
+ * Return the previous element of the list, or NULL if we've reached the start.
+ */
+struct dm_list *dm_list_prev(const struct dm_list *head, const struct dm_list *elem);
+
+/*
+ * Return the next element of the list, or NULL if we've reached the end.
+ */
+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'
+ * contained in a structure of type t, return the containing structure.
+ */
+#define dm_list_struct_base(v, t, head) \
+ ((t *)((const char *)(v) - (const char *)&((t *) 0)->head))
+
+/*
+ * Given the address v of an instance of 'struct dm_list list' contained in
+ * a structure of type t, return the containing structure.
+ */
+#define dm_list_item(v, t) dm_list_struct_base((v), t, list)
+
+/*
+ * Given the address v of one known element e in a known structure of type t,
+ * return another element f.
+ */
+#define dm_struct_field(v, t, e, f) \
+ (((t *)((uintptr_t)(v) - (uintptr_t)&((t *) 0)->e))->f)
+
+/*
+ * Given the address v of a known element e in a known structure of type t,
+ * return the list head 'list'
+ */
+#define dm_list_head(v, t, e) dm_struct_field(v, t, e, list)
+
+/*
+ * Set v to each element of a list in turn.
+ */
+#define dm_list_iterate(v, head) \
+ for (v = (head)->n; v != head; v = v->n)
+
+/*
+ * 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.
+ * If 'start' is 'head' it walks the list backwards.
+ */
+#define dm_list_uniterate(v, head, start) \
+ for (v = (start)->p; v != head; v = v->p)
+
+/*
+ * A safe way to walk a list and delete and free some elements along
+ * the way.
+ * t must be defined as a temporary variable of the same type as v.
+ */
+#define dm_list_iterate_safe(v, t, head) \
+ for (v = (head)->n, t = v->n; v != head; v = t, t = v->n)
+
+/*
+ * Walk a list, 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'.
+ */
+#define dm_list_iterate_items_gen(v, head, field) \
+ for (v = dm_list_struct_base((head)->n, __typeof__(*v), field); \
+ &v->field != (head); \
+ v = dm_list_struct_base(v->field.n, __typeof__(*v), field))
+
+/*
+ * Walk a list, 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.
+ */
+#define dm_list_iterate_items(v, head) dm_list_iterate_items_gen(v, (head), list)
+
+/*
+ * Walk a list, 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'.
+ * t must be defined as a temporary variable of the same type as v.
+ */
+#define dm_list_iterate_items_gen_safe(v, t, head, field) \
+ for (v = dm_list_struct_base((head)->n, __typeof__(*v), field), \
+ t = dm_list_struct_base(v->field.n, __typeof__(*v), field); \
+ &v->field != (head); \
+ v = t, t = dm_list_struct_base(v->field.n, __typeof__(*v), field))
+/*
+ * Walk a list, 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.
+ * t must be defined as a temporary variable of the same type as v.
+ */
+#define dm_list_iterate_items_safe(v, t, head) \
+ dm_list_iterate_items_gen_safe(v, t, (head), list)
+
+/*
+ * 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'.
+ */
+#define dm_list_iterate_back_items_gen(v, head, field) \
+ for (v = dm_list_struct_base((head)->p, __typeof__(*v), field); \
+ &v->field != (head); \
+ v = dm_list_struct_base(v->field.p, __typeof__(*v), field))
+
+/*
+ * 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.
+ */
+#define dm_list_iterate_back_items(v, head) dm_list_iterate_back_items_gen(v, (head), list)
+
+/*
+ * Return the number of elements in a list by walking it.
+ */
+unsigned int dm_list_size(const struct dm_list *head);
+
+//----------------------------------------------------------------
+
+#endif
diff --git a/device_mapper/Makefile b/device_mapper/Makefile
index b246f82..4036cb4 100644
--- a/device_mapper/Makefile
+++ b/device_mapper/Makefile
@@ -13,7 +13,6 @@
DEVICE_MAPPER_SOURCE=\
device_mapper/datastruct/bitset.c \
device_mapper/datastruct/hash.c \
- device_mapper/datastruct/list.c \
device_mapper/libdm-common.c \
device_mapper/libdm-config.c \
device_mapper/libdm-deptree.c \
diff --git a/device_mapper/datastruct/list.c b/device_mapper/datastruct/list.c
deleted file mode 100644
index bda8027..0000000
--- a/device_mapper/datastruct/list.c
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
- * Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
- *
- * This file is part of LVM2.
- *
- * This copyrighted material is made available to anyone wishing to use,
- * modify, copy, or redistribute it subject to the terms and conditions
- * of the GNU Lesser General Public License v.2.1.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include "device_mapper/misc/dmlib.h"
-#include <assert.h>
-
-/*
- * Initialise a list before use.
- * The list head's next and previous pointers point back to itself.
- */
-void dm_list_init(struct dm_list *head)
-{
- head->n = head->p = head;
-}
-
-/*
- * Insert an element before 'head'.
- * If 'head' is the list head, this adds an element to the end of the list.
- */
-void dm_list_add(struct dm_list *head, struct dm_list *elem)
-{
- assert(head->n);
-
- elem->n = head;
- elem->p = head->p;
-
- head->p->n = elem;
- head->p = elem;
-}
-
-/*
- * Insert an element after 'head'.
- * If 'head' is the list head, this adds an element to the front of the list.
- */
-void dm_list_add_h(struct dm_list *head, struct dm_list *elem)
-{
- assert(head->n);
-
- elem->n = head->n;
- elem->p = head;
-
- head->n->p = elem;
- head->n = elem;
-}
-
-/*
- * Delete an element from its list.
- * Note that this doesn't change the element itself - it may still be safe
- * to follow its pointers.
- */
-void dm_list_del(struct dm_list *elem)
-{
- elem->n->p = elem->p;
- elem->p->n = elem->n;
-}
-
-/*
- * Remove an element from existing list and insert before 'head'.
- */
-void dm_list_move(struct dm_list *head, struct dm_list *elem)
-{
- dm_list_del(elem);
- dm_list_add(head, elem);
-}
-
-/*
- * Is the list empty?
- */
-int dm_list_empty(const struct dm_list *head)
-{
- return head->n == head;
-}
-
-/*
- * Is this the first element of the list?
- */
-int dm_list_start(const struct dm_list *head, const struct dm_list *elem)
-{
- return elem->p == head;
-}
-
-/*
- * Is this the last element of the list?
- */
-int dm_list_end(const struct dm_list *head, const struct dm_list *elem)
-{
- return elem->n == head;
-}
-
-/*
- * Return first element of the list or NULL if empty
- */
-struct dm_list *dm_list_first(const struct dm_list *head)
-{
- return (dm_list_empty(head) ? NULL : head->n);
-}
-
-/*
- * Return last element of the list or NULL if empty
- */
-struct dm_list *dm_list_last(const struct dm_list *head)
-{
- return (dm_list_empty(head) ? NULL : head->p);
-}
-
-/*
- * Return the previous element of the list, or NULL if we've reached the start.
- */
-struct dm_list *dm_list_prev(const struct dm_list *head, const struct dm_list *elem)
-{
- return (dm_list_start(head, elem) ? NULL : elem->p);
-}
-
-/*
- * Return the next element of the list, or NULL if we've reached the end.
- */
-struct dm_list *dm_list_next(const struct dm_list *head, const struct dm_list *elem)
-{
- return (dm_list_end(head, elem) ? NULL : elem->n);
-}
-
-/*
- * Return the number of elements in a list by walking it.
- */
-unsigned int dm_list_size(const struct dm_list *head)
-{
- unsigned int s = 0;
- const struct dm_list *v;
-
- dm_list_iterate(v, head)
- s++;
-
- return s;
-}
-
-/*
- * Join two lists together.
- * This moves all the elements of the list 'head1' to the end of the list
- * 'head', leaving 'head1' empty.
- */
-void dm_list_splice(struct dm_list *head, struct dm_list *head1)
-{
- assert(head->n);
- assert(head1->n);
-
- if (dm_list_empty(head1))
- return;
-
- head1->p->n = head;
- head1->n->p = head->p;
-
- head->p->n = head1->n;
- head->p = head1->p;
-
- dm_list_init(head1);
-}
diff --git a/device_mapper/libdevmapper.h b/device_mapper/libdevmapper.h
index 2438f74..f7ff4ce 100644
--- a/device_mapper/libdevmapper.h
+++ b/device_mapper/libdevmapper.h
@@ -31,6 +31,10 @@
#include <stdlib.h>
#include <stdio.h>
+#include "base/data-struct/list.h"
+
+#include "base/data-struct/list.h"
+
#ifndef __GNUC__
# define __typeof__ typeof
#endif
@@ -2366,211 +2370,6 @@ void *dm_hash_lookup_with_count(struct dm_hash_table *t, const char *key, int *c
for (v = dm_hash_get_first((h)); v; \
v = dm_hash_get_next((h), v))
-/****************
- * list functions
- ****************/
-
-/*
- * A list consists of a list head plus elements.
- * Each element has 'next' and 'previous' pointers.
- * The list head's pointers point to the first and the last element.
- */
-
-struct dm_list {
- struct dm_list *n, *p;
-};
-
-/*
- * String list.
- */
-struct dm_str_list {
- struct dm_list list;
- const char *str;
-};
-
-/*
- * Initialise a list before use.
- * The list head's next and previous pointers point back to itself.
- */
-#define DM_LIST_HEAD_INIT(name) { &(name), &(name) }
-#define DM_LIST_INIT(name) struct dm_list name = DM_LIST_HEAD_INIT(name)
-void dm_list_init(struct dm_list *head);
-
-/*
- * Insert an element before 'head'.
- * If 'head' is the list head, this adds an element to the end of the list.
- */
-void dm_list_add(struct dm_list *head, struct dm_list *elem);
-
-/*
- * Insert an element after 'head'.
- * If 'head' is the list head, this adds an element to the front of the list.
- */
-void dm_list_add_h(struct dm_list *head, struct dm_list *elem);
-
-/*
- * Delete an element from its list.
- * Note that this doesn't change the element itself - it may still be safe
- * to follow its pointers.
- */
-void dm_list_del(struct dm_list *elem);
-
-/*
- * Remove an element from existing list and insert before 'head'.
- */
-void dm_list_move(struct dm_list *head, struct dm_list *elem);
-
-/*
- * Join 'head1' to the end of 'head'.
- */
-void dm_list_splice(struct dm_list *head, struct dm_list *head1);
-
-/*
- * Is the list empty?
- */
-int dm_list_empty(const struct dm_list *head);
-
-/*
- * Is this the first element of the list?
- */
-int dm_list_start(const struct dm_list *head, const struct dm_list *elem);
-
-/*
- * Is this the last element of the list?
- */
-int dm_list_end(const struct dm_list *head, const struct dm_list *elem);
-
-/*
- * Return first element of the list or NULL if empty
- */
-struct dm_list *dm_list_first(const struct dm_list *head);
-
-/*
- * Return last element of the list or NULL if empty
- */
-struct dm_list *dm_list_last(const struct dm_list *head);
-
-/*
- * Return the previous element of the list, or NULL if we've reached the start.
- */
-struct dm_list *dm_list_prev(const struct dm_list *head, const struct dm_list *elem);
-
-/*
- * Return the next element of the list, or NULL if we've reached the end.
- */
-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'
- * contained in a structure of type t, return the containing structure.
- */
-#define dm_list_struct_base(v, t, head) \
- ((t *)((const char *)(v) - (const char *)&((t *) 0)->head))
-
-/*
- * Given the address v of an instance of 'struct dm_list list' contained in
- * a structure of type t, return the containing structure.
- */
-#define dm_list_item(v, t) dm_list_struct_base((v), t, list)
-
-/*
- * Given the address v of one known element e in a known structure of type t,
- * return another element f.
- */
-#define dm_struct_field(v, t, e, f) \
- (((t *)((uintptr_t)(v) - (uintptr_t)&((t *) 0)->e))->f)
-
-/*
- * Given the address v of a known element e in a known structure of type t,
- * return the list head 'list'
- */
-#define dm_list_head(v, t, e) dm_struct_field(v, t, e, list)
-
-/*
- * Set v to each element of a list in turn.
- */
-#define dm_list_iterate(v, head) \
- for (v = (head)->n; v != head; v = v->n)
-
-/*
- * 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.
- * If 'start' is 'head' it walks the list backwards.
- */
-#define dm_list_uniterate(v, head, start) \
- for (v = (start)->p; v != head; v = v->p)
-
-/*
- * A safe way to walk a list and delete and free some elements along
- * the way.
- * t must be defined as a temporary variable of the same type as v.
- */
-#define dm_list_iterate_safe(v, t, head) \
- for (v = (head)->n, t = v->n; v != head; v = t, t = v->n)
-
-/*
- * Walk a list, 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'.
- */
-#define dm_list_iterate_items_gen(v, head, field) \
- for (v = dm_list_struct_base((head)->n, __typeof__(*v), field); \
- &v->field != (head); \
- v = dm_list_struct_base(v->field.n, __typeof__(*v), field))
-
-/*
- * Walk a list, 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.
- */
-#define dm_list_iterate_items(v, head) dm_list_iterate_items_gen(v, (head), list)
-
-/*
- * Walk a list, 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'.
- * t must be defined as a temporary variable of the same type as v.
- */
-#define dm_list_iterate_items_gen_safe(v, t, head, field) \
- for (v = dm_list_struct_base((head)->n, __typeof__(*v), field), \
- t = dm_list_struct_base(v->field.n, __typeof__(*v), field); \
- &v->field != (head); \
- v = t, t = dm_list_struct_base(v->field.n, __typeof__(*v), field))
-/*
- * Walk a list, 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.
- * t must be defined as a temporary variable of the same type as v.
- */
-#define dm_list_iterate_items_safe(v, t, head) \
- dm_list_iterate_items_gen_safe(v, t, (head), list)
-
-/*
- * 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'.
- */
-#define dm_list_iterate_back_items_gen(v, head, field) \
- for (v = dm_list_struct_base((head)->p, __typeof__(*v), field); \
- &v->field != (head); \
- v = dm_list_struct_base(v->field.p, __typeof__(*v), field))
-
-/*
- * 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.
- */
-#define dm_list_iterate_back_items(v, head) dm_list_iterate_back_items_gen(v, (head), list)
-
-/*
- * Return the number of elements in a list by walking it.
- */
-unsigned int dm_list_size(const struct dm_list *head);
-
/*********
* selinux
*********/
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=bb7c064b23c90e32ab4e8…
Commit: bb7c064b23c90e32ab4e8da610fca2c4e7bac73e
Parent: c93e0932e8d3005d2b0f6e1e66f7fba72f3ac950
Author: Zdenek Kabelac <zkabelac(a)redhat.com>
AuthorDate: Fri Jun 8 12:51:27 2018 +0200
Committer: Zdenek Kabelac <zkabelac(a)redhat.com>
CommitterDate: Fri Jun 8 14:37:31 2018 +0200
tests: initial testing code for lvs while pvmove runs
---
test/shell/pvmove-lvs.sh | 35 +++++++++++++++++++++++++++++++++++
1 files changed, 35 insertions(+), 0 deletions(-)
diff --git a/test/shell/pvmove-lvs.sh b/test/shell/pvmove-lvs.sh
new file mode 100644
index 0000000..754f86a
--- /dev/null
+++ b/test/shell/pvmove-lvs.sh
@@ -0,0 +1,35 @@
+#!/usr/bin/env bash
+
+# Copyright (C) 2018 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+test_description="ensure pvmove works with lvs"
+SKIP_WITH_LVMLOCKD=1
+
+. lib/inittest
+
+aux throttle_dm_mirror || skip
+
+aux prepare_vg 5 180
+
+lvcreate -aey -L30 -n $lv1 $vg "$dev1"
+lvextend -L+30 $vg/$lv1 "$dev2"
+lvextend -L+30 $vg/$lv1 "$dev1"
+lvextend -L+30 $vg/$lv1 "$dev2"
+lvextend -L+30 $vg/$lv1 "$dev1"
+
+pvmove -b "$dev1" "$dev5" 2>&1 | tee out
+
+#lvchange -an $vg/$lv1
+lvs -a $vg
+
+pvmove --abort
+
+lvremove -ff $vg
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=5cb4b2a42465f2430c98c…
Commit: 5cb4b2a42465f2430c98c8966adeacb0f6a26af3
Parent: 1f5f8382ae09e3ba95486903cf86b29641a0023d
Author: Zdenek Kabelac <zkabelac(a)redhat.com>
AuthorDate: Fri Jun 8 12:37:07 2018 +0200
Committer: Zdenek Kabelac <zkabelac(a)redhat.com>
CommitterDate: Fri Jun 8 14:37:29 2018 +0200
cache: cleaner policy also uses fmt2
Format 2 is also with cleaner policy.
---
WHATS_NEW | 1 +
lib/metadata/cache_manip.c | 5 +++--
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/WHATS_NEW b/WHATS_NEW
index a17902d..162b584 100644
--- a/WHATS_NEW
+++ b/WHATS_NEW
@@ -1,5 +1,6 @@
Version 3.0.0
=============
+ Cache can uses metadata format 2 with cleaner policy.
Fix check if resized PV can also fit metadata area.
Avoid showing internal error in lvs output or pvmoved LVs.
Remove clvmd
diff --git a/lib/metadata/cache_manip.c b/lib/metadata/cache_manip.c
index fe91fee..c3bd2ec 100644
--- a/lib/metadata/cache_manip.c
+++ b/lib/metadata/cache_manip.c
@@ -843,12 +843,13 @@ int cache_set_metadata_format(struct lv_segment *seg, cache_metadata_format_t fo
/*
* If policy is unselected, but format 2 is selected, policy smq is enforced.
- * ATM no other then smq policy is allowed to select format 2.
+ * ATM no other then smq & cleaner policy is allowed to select format 2.
*/
if (!seg->policy_name) {
if (format == CACHE_METADATA_FORMAT_2)
seg->policy_name = "smq";
- } else if (strcmp(seg->policy_name, "smq")) {
+ } else if (strcmp(seg->policy_name, "smq") &&
+ strcmp(seg->policy_name, "cleaner")) {
seg->cache_metadata_format = CACHE_METADATA_FORMAT_1;
return 1;
}
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=0c62ae3f8920ebb53397b…
Commit: 0c62ae3f8920ebb53397b2a904aa2e3dd3e73bed
Parent: b67ef90438052d3e01f563cdd25e2268f4e02c20
Author: Zdenek Kabelac <zkabelac(a)redhat.com>
AuthorDate: Mon Jun 4 11:05:28 2018 +0200
Committer: Zdenek Kabelac <zkabelac(a)redhat.com>
CommitterDate: Fri Jun 8 14:35:42 2018 +0200
pvmove: improve lvs
When pvmoving LV - the target for LV is a mirror so the validation
that checked the type is matching was incorrect.
While we need a more generic enhancment of LVS output for pvmoved LVs,
for now at least stop showing internal errors and 'X' symbols in attrs.
---
WHATS_NEW | 1 +
lib/activate/dev_manager.c | 3 ++-
2 files changed, 3 insertions(+), 1 deletions(-)
diff --git a/WHATS_NEW b/WHATS_NEW
index d75ab1b..a727a40 100644
--- a/WHATS_NEW
+++ b/WHATS_NEW
@@ -1,5 +1,6 @@
Version 3.0.0
=============
+ Avoid showing internal error in lvs output or pvmoved LVs.
Remove clvmd
Remove lvmlib (api)
lvconvert: provide possible layouts between linear and striped/raid
diff --git a/lib/activate/dev_manager.c b/lib/activate/dev_manager.c
index 5dd0e6f..2688bcf 100644
--- a/lib/activate/dev_manager.c
+++ b/lib/activate/dev_manager.c
@@ -178,7 +178,8 @@ static int _get_segment_status_from_target_params(const char *target_name,
}
/* Validate target_name segtype from DM table with lvm2 metadata segtype */
- if (strcmp(segtype->name, target_name) &&
+ if (!lv_is_locked(seg->lv) &&
+ strcmp(segtype->name, target_name) &&
/* If kernel's type isn't an exact match is it compatible? */
(!segtype->ops->target_status_compatible ||
!segtype->ops->target_status_compatible(target_name))) {
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=e6bb780d24246666fa059…
Commit: e6bb780d24246666fa05948ec449a8137280b443
Parent: c7c7017f0c12ebab4e1aef9d9a9fabd4ec2adfe3
Author: David Teigland <teigland(a)redhat.com>
AuthorDate: Thu Jun 7 15:33:02 2018 -0500
Committer: David Teigland <teigland(a)redhat.com>
CommitterDate: Thu Jun 7 16:47:15 2018 -0500
Rework lock-override options and locking_type settings
The last commit related to this was incomplete:
"Implement lock-override options without locking type"
This is further reworking and reduction of the locking.[ch]
layer which handled all clustering, but is now only used
for file locking. The "locking types" that this layer
implemented were removed previously, leaving only the
standard file locking. (Some cluster-related artifacts
remain to be cleared out later.)
Command options to override or modify locking behavior
are reimplemented here without using the locking types.
Also, deprecated locking_type values are recognized,
and implemented as if one of the equivalent override
options was set.
Options that override file locking are:
. --nolocking disables all file locking.
. --readonly grants read lock requests without actually
taking a file lock, and refuses write lock requests.
. --ignorelockingfailure tries to set up file locks and
uses them normally if possible. When not possible, it
behaves like --readonly, but allows activation.
. --sysinit is the same as ignorelockingfailure.
. global/metadata_read_only acquires actual read file
locks, and refuses write lock requests.
(Some of these options could probably be deprecated
because they were added as workarounds to various
locking_type behaviors that are now deprecated.)
The locking_type setting now has one valid value: 1 which
refers to standard file locking. Configs that contain
deprecated values are recognized and still work in
largely the same way:
. 0 disabled all locking, now implemented like --nolocking
is set. Allow the nolocking option in all commands.
. 1 is the normal file locking setting and is unchanged.
. 2 was for external locking which was not used, and
reverts to normal file locking.
. 3 was for cluster/clvm. This reverts to normal file
locking, and prints messages about lvmlockd.
. 4 was equivalent to readonly, now implemented like
--readonly is set.
. 5 disabled all locking, now implemented like
--nolocking is set.
---
lib/commands/toolcontext.h | 1 +
lib/config/config_settings.h | 43 +--------
lib/locking/locking.c | 216 ++++++++++++++++++++++++------------------
lib/locking/locking.h | 2 +-
lib/misc/lvm-globals.c | 11 --
lib/misc/lvm-globals.h | 4 -
lib/misc/sharedlib.c | 2 +-
tools/command-lines.in | 4 +-
tools/lvmcmdline.c | 53 ++++++-----
9 files changed, 163 insertions(+), 173 deletions(-)
diff --git a/lib/commands/toolcontext.h b/lib/commands/toolcontext.h
index b46e301..fee35d4 100644
--- a/lib/commands/toolcontext.h
+++ b/lib/commands/toolcontext.h
@@ -172,6 +172,7 @@ struct cmd_context {
unsigned can_use_one_scan:1;
unsigned is_clvmd:1;
unsigned use_full_md_check:1;
+ unsigned is_activating:1;
/*
* Filtering.
diff --git a/lib/config/config_settings.h b/lib/config/config_settings.h
index 6cc7b15..32a6476 100644
--- a/lib/config/config_settings.h
+++ b/lib/config/config_settings.h
@@ -785,51 +785,16 @@ cfg(global_etc_CFG, "etc", global_CFG_SECTION, 0, CFG_TYPE_STRING, DEFAULT_ETC_D
"Location of /etc system configuration directory.\n")
cfg(global_locking_type_CFG, "locking_type", global_CFG_SECTION, 0, CFG_TYPE_INT, 1, vsn(1, 0, 0), NULL, 0, NULL,
- "Type of locking to use.\n"
- "#\n"
- "Accepted values:\n"
- " 0\n"
- " Turns off locking. Warning: this risks metadata corruption if\n"
- " commands run concurrently.\n"
- " 1\n"
- " LVM uses local file-based locking, the standard mode.\n"
- " 2\n"
- " LVM uses the external shared library locking_library.\n"
- " 3\n"
- " LVM uses built-in clustered locking with clvmd.\n"
- " This is incompatible with lvmetad. If use_lvmetad is enabled,\n"
- " LVM prints a warning and disables lvmetad use.\n"
- " 4\n"
- " LVM uses read-only locking which forbids any operations that\n"
- " might change metadata.\n"
- " 5\n"
- " Offers dummy locking for tools that do not need any locks.\n"
- " You should not need to set this directly; the tools will select\n"
- " when to use it instead of the configured locking_type.\n"
- " Do not use lvmetad or the kernel device-mapper driver with this\n"
- " locking type. It is used by the --readonly option that offers\n"
- " read-only access to Volume Group metadata that cannot be locked\n"
- " safely because it belongs to an inaccessible domain and might be\n"
- " in use, for example a virtual machine image or a disk that is\n"
- " shared by a clustered machine.\n"
- "#\n")
+ "This setting is no longer used.")
cfg(global_wait_for_locks_CFG, "wait_for_locks", global_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_WAIT_FOR_LOCKS, vsn(2, 2, 50), NULL, 0, NULL,
"When disabled, fail if a lock request would block.\n")
cfg(global_fallback_to_clustered_locking_CFG, "fallback_to_clustered_locking", global_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_FALLBACK_TO_CLUSTERED_LOCKING, vsn(2, 2, 42), NULL, 0, NULL,
- "Attempt to use built-in cluster locking if locking_type 2 fails.\n"
- "If using external locking (type 2) and initialisation fails, with\n"
- "this enabled, an attempt will be made to use the built-in clustered\n"
- "locking. Disable this if using a customised locking_library.\n")
+ "This setting is no longer used.\n")
cfg(global_fallback_to_local_locking_CFG, "fallback_to_local_locking", global_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_FALLBACK_TO_LOCAL_LOCKING, vsn(2, 2, 42), NULL, 0, NULL,
- "Use locking_type 1 (local) if locking_type 2 or 3 fail.\n"
- "If an attempt to initialise type 2 or type 3 locking failed, perhaps\n"
- "because cluster components such as clvmd are not running, with this\n"
- "enabled, an attempt will be made to use local file-based locking\n"
- "(type 1). If this succeeds, only commands against local VGs will\n"
- "proceed. VGs marked as clustered will be ignored.\n")
+ "This setting is no longer used.\n")
cfg(global_locking_dir_CFG, "locking_dir", global_CFG_SECTION, 0, CFG_TYPE_STRING, DEFAULT_LOCK_DIR, vsn(1, 0, 0), "@DEFAULT_LOCK_DIR@", 0, NULL,
"Directory to use for LVM command file locks.\n"
@@ -849,7 +814,7 @@ cfg(global_library_dir_CFG, "library_dir", global_CFG_SECTION, CFG_DEFAULT_UNDEF
"Search this directory first for shared libraries.\n")
cfg(global_locking_library_CFG, "locking_library", global_CFG_SECTION, CFG_ALLOW_EMPTY | CFG_DEFAULT_COMMENTED, CFG_TYPE_STRING, DEFAULT_LOCKING_LIB, vsn(1, 0, 0), NULL, 0, NULL,
- "The external locking library to use for locking_type 2.\n")
+ "This setting is no longer used.\n")
cfg(global_abort_on_internal_errors_CFG, "abort_on_internal_errors", global_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_ABORT_ON_INTERNAL_ERRORS, vsn(2, 2, 57), NULL, 0, NULL,
"Abort a command that encounters an internal error.\n"
diff --git a/lib/locking/locking.c b/lib/locking/locking.c
index 4c27a81..7e499bb 100644
--- a/lib/locking/locking.c
+++ b/lib/locking/locking.c
@@ -36,6 +36,8 @@ static int _vg_write_lock_held = 0; /* VG write lock held? */
static int _blocking_supported = 0;
static int _file_locking_readonly = 0;
static int _file_locking_sysinit = 0;
+static int _file_locking_ignorefail = 0;
+static int _file_locking_failed = 0;
static void _unblock_signals(void)
{
@@ -85,27 +87,65 @@ static void _update_vg_lock_count(const char *resource, uint32_t flags)
}
/*
- * Select a locking type
- * type: locking type; if < 0, then read config tree value
+ * A mess of options have been introduced over time to override
+ * or tweak the behavior of file locking. These options are
+ * allowed in different but overlapping sets of commands
+ * (see command-lines.in)
+ *
+ * --nolocking
+ *
+ * Command won't try to set up or use file locks at all.
+ *
+ * --readonly
+ *
+ * Command will grant any read lock request, without trying
+ * to acquire an actual file lock. Command will refuse any
+ * write lock request.
+ *
+ * --ignorelockingfailure
+ *
+ * Command tries to set up file locks and will use them
+ * (both read and write) if successful. If command fails
+ * to set up file locks it falls back to readonly behavior
+ * above, while allowing activation.
+ *
+ * --sysinit
+ *
+ * The same as ignorelockingfailure.
+ *
+ * global/metadata_read_only
+ *
+ * The command acquires actual read locks and refuses
+ * write lock requests.
*/
-int init_locking(struct cmd_context *cmd, int file_locking_sysinit, int file_locking_readonly)
+
+int init_locking(struct cmd_context *cmd,
+ int file_locking_sysinit, int file_locking_readonly, int file_locking_ignorefail)
{
int suppress_messages = 0;
- if (getenv("LVM_SUPPRESS_LOCKING_FAILURE_MESSAGES"))
- suppress_messages = 1;
-
- if (file_locking_sysinit)
+ if (file_locking_sysinit || getenv("LVM_SUPPRESS_LOCKING_FAILURE_MESSAGES"))
suppress_messages = 1;
_blocking_supported = find_config_tree_bool(cmd, global_wait_for_locks_CFG, NULL);
_file_locking_readonly = file_locking_readonly;
_file_locking_sysinit = file_locking_sysinit;
+ _file_locking_ignorefail = file_locking_ignorefail;
+
+ log_debug("File locking settings: readonly:%d sysinit:%d ignorelockingfailure:%d global/metadata_read_only:%d global/wait_for_locks:%d.",
+ _file_locking_readonly, _file_locking_sysinit, _file_locking_ignorefail,
+ cmd->metadata_read_only, _blocking_supported);
+
+ if (!init_file_locking(&_locking, cmd, suppress_messages)) {
+ log_error_suppress(suppress_messages, "File locking initialisation failed.");
+
+ _file_locking_failed = 1;
- log_very_verbose("%sFile-based locking selected.", _blocking_supported ? "" : "Non-blocking ");
+ if (file_locking_sysinit || file_locking_ignorefail)
+ return 1;
- if (!init_file_locking(&_locking, cmd, suppress_messages))
- log_error_suppress(suppress_messages, "File-based locking initialisation failed.");
+ return 0;
+ }
return 1;
}
@@ -125,68 +165,12 @@ void fin_locking(void)
*/
static int _lock_vol(struct cmd_context *cmd, const char *resource, uint32_t flags)
{
- uint32_t lck_type = flags & LCK_TYPE_MASK;
- uint32_t lck_scope = flags & LCK_SCOPE_MASK;
int ret = 0;
block_signals(flags);
- assert(resource);
+ ret = _locking.lock_resource(cmd, resource, flags, NULL);
- if (!*resource) {
- log_error(INTERNAL_ERROR "Use of P_orphans is deprecated.");
- goto out;
- }
-
- if ((is_orphan_vg(resource) || is_global_vg(resource)) && (flags & LCK_CACHE)) {
- log_error(INTERNAL_ERROR "P_%s referenced.", resource);
- goto out;
- }
-
- if ((lck_type == LCK_WRITE) && (lck_scope == LCK_VG) && !(flags & LCK_CACHE) &&
- strcmp(resource, VG_GLOBAL)) {
-
- /* read only locking set in lvm.conf metadata_read_only */
-
- if (cmd->metadata_read_only) {
- log_error("Operation prohibited while global/metadata_read_only is set.");
- goto out;
- }
-
- /* read only locking set with option --readonly */
-
- if (_file_locking_readonly) {
- log_error("Read-only locking specified. Write locks are prohibited.");
- goto out;
- }
-
- /* read only locking (except activation) set with option --sysinit */
- /* FIXME: sysinit is intended to allow activation, add that exception here */
-
- if (_file_locking_sysinit) {
- log_error("Read-only sysinit locking specified. Write locks are prohibited.");
- goto out;
- }
- }
-
- if ((ret = _locking.lock_resource(cmd, resource, flags, NULL))) {
- if (lck_scope == LCK_VG && !(flags & LCK_CACHE)) {
- if (lck_type != LCK_UNLOCK)
- lvmcache_lock_vgname(resource, lck_type == LCK_READ);
- dev_reset_error_count(cmd);
- }
-
- _update_vg_lock_count(resource, flags);
- } else
- stack;
-
- /* If unlocking, always remove lock from lvmcache even if operation failed. */
- if (lck_scope == LCK_VG && !(flags & LCK_CACHE) && lck_type == LCK_UNLOCK) {
- lvmcache_unlock_vgname(resource);
- if (!ret)
- _update_vg_lock_count(resource, flags);
- }
-out:
_unblock_signals();
return ret;
@@ -195,52 +179,102 @@ out:
int lock_vol(struct cmd_context *cmd, const char *vol, uint32_t flags, const struct logical_volume *lv)
{
char resource[258] __attribute__((aligned(8)));
- int lck_type = flags & LCK_TYPE_MASK;
+ uint32_t lck_type = flags & LCK_TYPE_MASK;
+ uint32_t lck_scope = flags & LCK_SCOPE_MASK;
- /* file locking disabled */
+ if (!_blocking_supported)
+ flags |= LCK_NONBLOCK;
+
+ if (is_orphan_vg(vol))
+ vol = VG_ORPHANS;
+
+ if (!dm_strncpy(resource, vol, sizeof(resource))) {
+ log_error(INTERNAL_ERROR "Resource name %s is too long.", vol);
+ return 0;
+ }
+
+ /*
+ * File locking is disabled by --nolocking.
+ */
if (!_locking.flags)
- return 1;
+ goto out_hold;
+
+ /*
+ * When file locking could not be initialized, --ignorelockingfailure
+ * and --sysinit behave like --readonly, but allow activation.
+ */
+ if (_file_locking_failed && (_file_locking_sysinit || _file_locking_ignorefail)) {
+ if (lck_type != LCK_WRITE)
+ goto out_hold;
+
+ if (cmd->is_activating && (lck_scope == LCK_VG) && !(flags & LCK_CACHE) && strcmp(vol, VG_GLOBAL))
+ goto out_hold;
- if (flags == LCK_NONE) {
- log_debug_locking(INTERNAL_ERROR "%s: LCK_NONE lock requested", vol);
- return 1;
+ goto out_fail;
}
- switch (flags & LCK_SCOPE_MASK) {
- case LCK_VG:
- if (!_blocking_supported)
- flags |= LCK_NONBLOCK;
-
- /* Global VG_ORPHANS lock covers all orphan formats. */
- if (is_orphan_vg(vol))
- vol = VG_ORPHANS;
- break;
- default:
- log_error("Unrecognised lock scope: %d",
- flags & LCK_SCOPE_MASK);
- return 0;
+ /*
+ * When --readonly is set, grant read lock requests without trying to
+ * acquire an actual lock, and refuse write lock requests.
+ */
+ if (_file_locking_readonly) {
+ if (lck_type != LCK_WRITE)
+ goto out_hold;
+
+ log_error("Operation prohibited while --readonly is set.");
+ goto out_fail;
}
- if (!dm_strncpy(resource, vol, sizeof(resource))) {
- log_error(INTERNAL_ERROR "Resource name %s is too long.", vol);
- return 0;
+ /*
+ * When global/metadata_read_only is set, acquire actual read locks and
+ * refuse write lock requests.
+ */
+ if (cmd->metadata_read_only) {
+ if ((lck_type == LCK_WRITE) && (lck_scope == LCK_VG) && !(flags & LCK_CACHE) && strcmp(vol, VG_GLOBAL)) {
+ log_error("Operation prohibited while global/metadata_read_only is set.");
+ goto out_fail;
+ }
+
+ /* continue and acquire a read file lock */
}
if (!_lock_vol(cmd, resource, flags))
- return_0;
+ goto out_fail;
/*
+ * FIXME: I don't think we need this any more.
* If a real lock was acquired (i.e. not LCK_CACHE),
* perform an immediate unlock unless LCK_HOLD was requested.
*/
+
if ((lck_type == LCK_NULL) || (lck_type == LCK_UNLOCK) ||
(flags & (LCK_CACHE | LCK_HOLD)))
- return 1;
+ goto out_hold;
if (!_lock_vol(cmd, resource, (flags & ~LCK_TYPE_MASK) | LCK_UNLOCK))
return_0;
+ return 1;
+
+
+out_hold:
+ /*
+ * FIXME: other parts of the code want to check if a VG is
+ * locked by looking in lvmcache. They shouldn't need to
+ * do that, and we should be able to remove this.
+ */
+ if ((lck_scope == LCK_VG) && !(flags & LCK_CACHE) && (lck_type != LCK_UNLOCK))
+ lvmcache_lock_vgname(resource, lck_type == LCK_READ);
+ else if ((lck_scope == LCK_VG) && !(flags & LCK_CACHE) && (lck_type == LCK_UNLOCK))
+ lvmcache_unlock_vgname(resource);
+ /* FIXME: we shouldn't need to keep track of this either. */
+ _update_vg_lock_count(resource, flags);
return 1;
+
+out_fail:
+ if (lck_type == LCK_UNLOCK)
+ _update_vg_lock_count(resource, flags);
+ return 0;
}
/* Lock a list of LVs */
diff --git a/lib/locking/locking.h b/lib/locking/locking.h
index 422f1d8..96f7d86 100644
--- a/lib/locking/locking.h
+++ b/lib/locking/locking.h
@@ -21,7 +21,7 @@
struct logical_volume;
-int init_locking(struct cmd_context *cmd, int suppress_messages, int only_read_locks);
+int init_locking(struct cmd_context *cmd, int file_locking_sysinit, int file_locking_readonly, int file_locking_ignorefail);
void fin_locking(void);
void reset_locking(void);
int vg_write_lock_held(void);
diff --git a/lib/misc/lvm-globals.c b/lib/misc/lvm-globals.c
index b76ddb9..9377588 100644
--- a/lib/misc/lvm-globals.c
+++ b/lib/misc/lvm-globals.c
@@ -34,7 +34,6 @@ static int _trust_cache = 0; /* Don't scan when incomplete VGs encountered */
static int _debug_level = 0;
static int _debug_classes_logged = 0;
static int _log_cmd_name = 0;
-static int _ignorelockingfailure = 0;
static int _security_level = SECURITY_LEVEL;
static char _cmd_name[30] = "";
static int _mirror_in_sync = 0;
@@ -106,11 +105,6 @@ void init_trust_cache(int trustcache)
_trust_cache = trustcache;
}
-void init_ignorelockingfailure(int level)
-{
- _ignorelockingfailure = level;
-}
-
void init_security_level(int level)
{
_security_level = level;
@@ -267,11 +261,6 @@ int background_polling(void)
return _background_polling;
}
-int ignorelockingfailure(void)
-{
- return _ignorelockingfailure;
-}
-
int security_level(void)
{
return _security_level;
diff --git a/lib/misc/lvm-globals.h b/lib/misc/lvm-globals.h
index b383891..55a9399 100644
--- a/lib/misc/lvm-globals.h
+++ b/lib/misc/lvm-globals.h
@@ -35,8 +35,6 @@ void init_trust_cache(int trustcache);
void init_debug(int level);
void init_debug_classes_logged(int classes);
void init_cmd_name(int status);
-void init_ignorelockingfailure(int level);
-void init_lockingfailed(int level);
void init_security_level(int level);
void init_mirror_in_sync(int in_sync);
void init_dmeventd_monitor(int reg);
@@ -69,8 +67,6 @@ int verbose_level(void);
int silent_mode(void);
int debug_level(void);
int debug_class_is_logged(int class);
-int ignorelockingfailure(void);
-int lockingfailed(void);
int security_level(void);
int mirror_in_sync(void);
int background_polling(void);
diff --git a/lib/misc/sharedlib.c b/lib/misc/sharedlib.c
index d594470..4c2d5d9 100644
--- a/lib/misc/sharedlib.c
+++ b/lib/misc/sharedlib.c
@@ -58,7 +58,7 @@ void *load_shared_library(struct cmd_context *cmd, const char *libname,
log_very_verbose("Opening shared %s library %s", desc, path);
if (!(library = dlopen(path, RTLD_LAZY | RTLD_GLOBAL))) {
- if (silent && ignorelockingfailure())
+ if (silent)
log_verbose("Unable to open external %s library %s: %s",
desc, path, dlerror());
else
diff --git a/tools/command-lines.in b/tools/command-lines.in
index 409f01c..ad710a4 100644
--- a/tools/command-lines.in
+++ b/tools/command-lines.in
@@ -187,7 +187,7 @@
# OO_ALL is included in every command automatically.
#
OO_ALL: --commandprofile String, --config String, --debug,
---driverloaded Bool, --help, --lockopt String, --longhelp, --profile String, --quiet,
+--driverloaded Bool, --help, --nolocking, --lockopt String, --longhelp, --profile String, --quiet,
--verbose, --version, --yes, --test
#
@@ -195,7 +195,7 @@ OO_ALL: --commandprofile String, --config String, --debug,
#
OO_REPORT: --aligned, --all, --binary, --configreport ConfigReport, --foreign,
--ignorelockingfailure, --ignoreskippedcluster, --logonly,
---nameprefixes, --noheadings, --nolocking, --nosuffix,
+--nameprefixes, --noheadings, --nosuffix,
--options String, --readonly, --reportformat ReportFmt, --rows,
--select String, --separator String, --shared, --sort String,
--trustcache, --unbuffered, --units Units, --unquoted
diff --git a/tools/lvmcmdline.c b/tools/lvmcmdline.c
index 788f007..2578113 100644
--- a/tools/lvmcmdline.c
+++ b/tools/lvmcmdline.c
@@ -2323,11 +2323,6 @@ static int _get_current_settings(struct cmd_context *cmd)
return EINVALID_CMD_LINE;
}
- if (arg_is_set(cmd, ignorelockingfailure_ARG) || arg_is_set(cmd, sysinit_ARG))
- init_ignorelockingfailure(1);
- else
- init_ignorelockingfailure(0);
-
cmd->ignore_clustered_vgs = arg_is_set(cmd, ignoreskippedcluster_ARG);
cmd->include_foreign_vgs = arg_is_set(cmd, foreign_ARG) ? 1 : 0;
cmd->include_shared_vgs = arg_is_set(cmd, shared_ARG) ? 1 : 0;
@@ -2734,9 +2729,8 @@ int lvm_run_command(struct cmd_context *cmd, int argc, char **argv)
const char *reason = NULL;
int ret = 0;
int locking_type;
- int file_locking_disable = 0;
- int file_locking_sysinit = 0;
- int file_locking_readonly = 0;
+ int nolocking = 0;
+ int readonly = 0;
int monitoring;
char *arg_new, *arg;
int i;
@@ -2865,6 +2859,10 @@ int lvm_run_command(struct cmd_context *cmd, int argc, char **argv)
if (arg_is_set(cmd, readonly_ARG))
cmd->metadata_read_only = 1;
+ if ((cmd->command->command_enum == vgchange_activate_CMD) ||
+ (cmd->command->command_enum == lvchange_activate_CMD))
+ cmd->is_activating = 1;
+
/*
* Now that all configs, profiles and command lines args are available,
* freshly calculate and apply all settings. Specific command line
@@ -2911,28 +2909,35 @@ int lvm_run_command(struct cmd_context *cmd, int argc, char **argv)
goto out;
}
- if (arg_is_set(cmd, nolocking_ARG) || _cmd_no_meta_proc(cmd))
- file_locking_disable = 1;
-
- else if (arg_is_set(cmd, sysinit_ARG))
- file_locking_sysinit = 1;
-
- else if (arg_is_set(cmd, readonly_ARG))
- file_locking_readonly = 1;
-
+ /* Defaults to 1 if not set. */
locking_type = find_config_tree_int(cmd, global_locking_type_CFG, NULL);
if (locking_type == 3)
- log_warn("WARNING: See lvmlockd(8) for information on using cluster/clvm VGs.");
+ log_warn("WARNING: see lvmlockd(8) for information on using cluster/clvm VGs.");
- if (locking_type != 1) {
- log_warn("WARNING: locking_type deprecated, using file locking.");
- locking_type = 1;
+ if ((locking_type == 0) || (locking_type == 5)) {
+ log_warn("WARNING: locking_type is deprecated, using --nolocking.");
+ nolocking = 1;
+
+ } else if (locking_type == 4) {
+ log_warn("WARNING: locking_type is deprecated, using --readonly.");
+ readonly = 1;
+
+ } else if (locking_type != 1) {
+ log_warn("WARNING: locking_type is deprecated, using file locking.");
}
- if (!file_locking_disable) {
- /* Set up file locking */
- if (!init_locking(cmd, file_locking_sysinit, file_locking_readonly)) {
+ if (arg_is_set(cmd, nolocking_ARG) || _cmd_no_meta_proc(cmd))
+ nolocking = 1;
+
+ if (arg_is_set(cmd, readonly_ARG))
+ readonly = 1;
+
+ if (nolocking) {
+ if (!_cmd_no_meta_proc(cmd))
+ log_warn("WARNING: File locking is disabled.");
+ } else {
+ if (!init_locking(cmd, arg_is_set(cmd, sysinit_ARG), readonly, arg_is_set(cmd, ignorelockingfailure_ARG))) {
ret = ECMD_FAILED;
goto_out;
}
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=2eda683a205d1ad2463cf…
Commit: 2eda683a205d1ad2463cf60d783bc63d9833717e
Parent: 232918fb86e1ee254ae5e4c9aece4615d828c9bb
Author: Joe Thornber <ejt(a)redhat.com>
AuthorDate: Mon Jun 4 15:37:35 2018 +0100
Committer: Joe Thornber <ejt(a)redhat.com>
CommitterDate: Mon Jun 4 15:37:35 2018 +0100
build: base/Makefile
.gitignore hid it.
---
base/Makefile | 29 +++++++++++++++++++++++++++++
1 files changed, 29 insertions(+), 0 deletions(-)
diff --git a/base/Makefile b/base/Makefile
new file mode 100644
index 0000000..27f539f
--- /dev/null
+++ b/base/Makefile
@@ -0,0 +1,29 @@
+# Copyright (C) 2018 Red Hat, Inc. All rights reserved.
+#
+# This file is part of the device-mapper userspace tools.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU Lesser General Public License v.2.1.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+BASE_SOURCE=\
+ base/data-struct/radix-tree.c \
+
+BASE_DEPENDS=$(addprefix $(top_builddir)/,$(subst .c,.d,$(BASE_SOURCE)))
+BASE_OBJECTS=$(addprefix $(top_builddir)/,$(subst .c,.o,$(BASE_SOURCE)))
+CLEAN_TARGETS+=$(BASE_DEPENDS) $(BASE_OBJECTS)
+
+-include $(BASE_DEPENDS)
+
+$(BASE_OBJECTS): INCLUDES+=-I$(top_srcdir)/base/
+
+$(top_builddir)/base/libbase.a: $(BASE_OBJECTS)
+ @echo " [AR] $@"
+ $(Q) $(RM) $@
+ $(Q) $(AR) rsv $@ $(BASE_OBJECTS) > /dev/null
+
+CLEAN_TARGETS+=$(top_builddir)/base/libbase.a