1
0
mirror of git://sourceware.org/git/lvm2.git synced 2025-09-17 21:44:24 +03:00

Compare commits

...

41 Commits

Author SHA1 Message Date
Bryn M. Reeves
73111bf33b doc: document --area, --region, and --group in dmstats.8.in 2016-07-05 19:29:51 +01:00
Bryn M. Reeves
3b6f7f00f7 dmstats: fix <backtrace> in _display_info_cols()
Remove a false <backtrace> in _display_info_cols(): it is not an
error if there are no regions to display.

Fixes commit e6724f03.
2016-07-05 19:29:51 +01:00
Bryn M. Reeves
42746c7026 libdm: fix <backtrace> in dm_stats_populate 2016-07-05 19:29:51 +01:00
Bryn M. Reeves
a9f1278b55 libdm: fix <backtrace> in dm_stats_get_nr_regions 2016-07-05 19:29:51 +01:00
Bryn M. Reeves
ced707df28 dmstats: rename --auxdata to --userdata 2016-07-05 19:29:51 +01:00
Bryn M. Reeves
6fb39fce21 dmstats: rename 'aux_data' to 'user_data'
Make it clear that the "aux data" presented in reports is the user
data stored in the field (and does not include any library-internal
state such as group descriptors) by renaming the field to user_data
and changing the heading to "UserData".
2016-07-05 19:29:51 +01:00
Bryn M. Reeves
d4d9da6207 libdm: clarify library's use of aux_data
Make it clear in libdevmapper.h, and in function argument names, that
libdm-stats uses the aux_data field internally and that any values set
for user_data are appended to the library values before being stored
with a region, and similarly, that internal data fields will be stripped
prior to returning any previously stored user_data.
2016-07-05 19:29:51 +01:00
Bryn M. Reeves
6d374475ce dmstats: replace --statstype with separate object switches
Replace --statstype=area,region,group with a separate switch for
each object type: --area, --region, --group. Omitting any object
type switch will use the defaults for the current command (regions
and groups for list, and regions, groups and areas for verbose list).
2016-07-05 19:29:51 +01:00
Bryn M. Reeves
cc6177284a dmstats: add 'statsname' and 'obj_type' to default stats fields 2016-07-05 19:29:51 +01:00
Bryn M. Reeves
c2e043f0c1 dmstats: rename 'type' field to 'obj_type'
Rename the field and remove whitespace from the column heading:

  "Object Type" -> "ObjType"
2016-07-05 19:29:51 +01:00
Bryn M. Reeves
59a3705577 dmstats: use 'statsname' and 'groupid' in default fields
Replace the 'name' field with 'statsname' in order to report alias
names for groups, and include the 'group_id' field between statsname
and the 'region_id' field to make it clear to the user when groups
are in use.
2016-07-05 19:29:51 +01:00
Bryn M. Reeves
76f7f28dac dmstats: convert 'delete' to dm_stats_foreach_region() 2016-07-05 19:29:50 +01:00
Bryn M. Reeves
c54da5e82a dmstats: convert 'print' to dm_stats_foreach_region() 2016-07-05 19:29:50 +01:00
Bryn M. Reeves
4b02aa2548 dmstats: convert 'clear' to dm_stats_foreach_region() 2016-07-05 19:29:50 +01:00
Bryn M. Reeves
e69a34c15e doc: update dmstats.8.in for groups 2016-07-05 19:29:50 +01:00
Bryn M. Reeves
3914229d60 dmstats: fix region deletion message
Make the use of 64-bit format macros consistent with other usage
and end the message with a '.'.
2016-07-05 19:29:50 +01:00
Bryn M. Reeves
e25c27e5c2 dmstats: accept --groupid for 'dmstats delete'
Allow deletion of a group and all the regions it contains with a
single 'dmstats delete' command.
2016-07-05 19:29:50 +01:00
Bryn M. Reeves
4ebc6b75cb dmstats: allow --statstype to override report defults 2016-07-05 19:29:50 +01:00
Bryn M. Reeves
b7cd377915 dmstats: report a list of members as a group's region_id
Instead of '-' print the member list in range notation (as stored
in aux_data).
2016-07-05 19:29:50 +01:00
Bryn M. Reeves
8dffd6d217 dmstats: report groups and region summaries
Walk avaiable groups and regions (in addition to areas) and report
aggregate statistics and properties.

A new switch is added to filter the type of obects inclued in the
report:

  --statstype={all,area,region,group}

The type of the current row is also available in a new
DR_STATS_META field 'type'.
2016-07-05 19:29:50 +01:00
Bryn M. Reeves
d94c956903 dmstats: do not walk regions if deleting a single id 2016-07-05 19:29:50 +01:00
Bryn M. Reeves
3626cf025d dmstats: add stats_name field
To allow the names used to describe statistics report objects to
change (for e.g to support groups and region and group aliases)
introduce a new "stats_name" field that evaluates to the correct
name for the object being reported.
2016-07-05 19:29:50 +01:00
Bryn M. Reeves
65606c90d9 dmstats: add group alias support 2016-07-05 19:29:50 +01:00
Bryn M. Reeves
973ad7434e dmstats: add 'group' and 'ungroup' commands
Add a pair of commands to create and delete stats groups:

  dmstats group --regions REGIONS

  dmstats ungroup --groupid ID

REGIONS specifies a list of regions to be included in the group.
Regions are specified as a comma separated list in order of
increasing region ID. Ranges may be specified as a hypen separated
pair of values giving the first and last member of the range.
2016-07-05 19:29:50 +01:00
Bryn M. Reeves
14868633cc dmstats: add group_id report field type 2016-07-05 19:29:50 +01:00
Bryn M. Reeves
5160936b15 libdm: allow deleting regions with dm_stats_delete_group()
Add a flag to dm_stats_delete_group() to allow optional deletion
of all regions belonging to the group being removed.
2016-07-05 19:29:50 +01:00
Bryn M. Reeves
d6aea67a17 libdm: add stats group and region iterators and properties
Add support do dm_stats_walk*() to walk over the set of
available groups using the cursor embedded in the dm_stats
handle, and to obtain the type of the object at the current
stats cursor location. A set of flags is introduced to
control which objects are visited:

    DM_STATS_WALK_AREA
    DM_STATS_WALK_REGION
    DM_STATS_WALK_GROUP
    DM_STATS_WALK_ALL

A final flag suppresses visits to regions that contain only a
single area - since the aggregate of such a region is idential
to the area it contains this allows these duplicates to be
filtered out:

    DM_STATS_WALK_SKIP_SINGLE_AREA

If flags are not initialised before beginning a walk the default
set matches the behaviour of previous versions of the library.

Also accept group identifiers as immediate arguments to the
counter, metric, and property functions by adding control
flags to the region and area identifiers passed in.

Region and area properties are mapped to their equivalents for
the group (for example: group size is reported as the sum of
all regions contained in the group). Counter and metric values
are aggregated for the region or group.
2016-07-05 19:29:50 +01:00
Bryn M. Reeves
a7ff854ec8 libdm: use defined constants for buffer sizes
Introduce constants for the buffer sizes that libdm-stats uses:
one for messages sent to the kernel, one for rows of response data
returned, and a pair for the "start+len" range and histogram bounds
strings.
2016-07-05 19:29:50 +01:00
Bryn M. Reeves
abb499c1c2 libdm: add statistics groups
Add a grouping facility to the libdm-stats library that allows the
user to bind several regions together as a group. Groups may be
used to aggregate data from several regions for reporting, or to
select and sort among large sets of regions.

A textual descriptor ("group tag") is associated with each group
and is stored in the first group member's aux_data field. The
tag contains the group member list and an optional alias for the
group, allowing the user to assign meaningful names to groups of
regions.

These descriptors are parsed in @stats_list message responses and
populate the resulting region and area tables with the group
structure.

Groups with overlapping regions are permitted but since this will
result in some events being counted more than once a warning is
printed in this case.

Nested and overlapping groups are not currently supported and
attempting to create these configurations results in error.
2016-07-05 19:29:50 +01:00
Bryn M. Reeves
728949c7bb libdm: rename 'region' to 'skip_region' in _stats_walk_next
In libdm-stats.c 'region' usually refers to a 'struct region*'.
Rename the argument to _stats_walk_start to avoid confusion.
2016-07-05 19:29:50 +01:00
Bryn M. Reeves
9420f82485 libdm: add enum based counter and metric calls
Add a new enum based interface for accessing counter and metric
values that uses a single function for each:

uint64_t dm_stats_get_counter(const struct dm_stats *dms,
                              dm_stats_counter_t counter
                              uint64_t region_id, uint64_t area_id);

int dm_stats_get_metric(const struct dm_stats *dms, int metric,
                        uint64_t region_id, uint64_t area_id,
                        double *value);

This simplifies the implementation of value aggregation for
groups of regions. The named function interface now calls the
enum interface internally so that all new functionality is
available regardless of the method used to retrieve values.
2016-07-05 19:29:50 +01:00
Bryn M. Reeves
f6a999b437 libdm: cache dm name in stats handle
Cache the device-mapper name of a bound device in the dm_stats
handle.

This will be used by stats groups to report a device name or
user defined alias for groups.
2016-07-05 19:29:50 +01:00
Bryn M. Reeves
ea2fb03750 libdm: rename dm_stats name, devno and uuid members
The device-mapper name, device numbers and uuid stored in the
dm_stats handle are used only to bind the handle to a specific
device in order to issue ioctls.

Rename them to "bind_*" to reflect this usage in preparation
for caching the device-mapper name of the bound device in the
dm_stats handle.

This will be used to allow optional aliases to be set for
dmstats groups.
2016-07-05 19:29:49 +01:00
Bryn M. Reeves
f883bca41b libdm: add dm_bitset_parse_list()
Add a function to parse a list of integer values and ranges into
a dm_bitset representation. Individual values signify that that bit
is set in the resulting mask and ranges are given as a pair of
start and end values, M-N, such that M and N are the first and
last members of the range (inclusive).

The implementation is based on the kernel's __bitmap_parselist()
that is used for cpumasks and other set configuration passed in
string form from user space.
2016-07-05 19:29:49 +01:00
Bryn M. Reeves
7145d666fc libdm: fix histogram pool user-after-free (CWE-825) 2016-07-05 19:29:49 +01:00
Heinz Mauelshagen
f3afd1bd13 vgsplit: fix moving RAID LVs to split off VG and check for LVs not to skip moving with other LV types 2016-07-05 15:39:57 +02:00
Heinz Mauelshagen
b1b32c900a Merge branch 'master' of ssh://git.fedorahosted.org/git/lvm2 2016-07-04 22:29:52 +02:00
Heinz Mauelshagen
7cec710e37 Merge branch 'master' of ssh://git.fedorahosted.org/git/lvm2 2016-06-29 16:34:42 +02:00
Peter Rajnoha
6c86d94219 libdm: report: fix field width calculation when using dm_report_column_headings
This fixes commit 0ba5f4b8e9 which moved
field recalculation (field width and sort position) from
dm_report_object to dm_report_output but it didn't handle the case when
dm_report_column_headings was used separately to report headings (before
dm_report_outpout call) and hence we ended up with intial widths for
fields in the headings.

If we're using dm_report_column_headings, we need to recalculate
fields if we haven't done so yet, the same way as we do in
dm_report_output.
2016-06-27 14:33:28 +02:00
Peter Rajnoha
05196373be reporter: simplify --configreport handling for -S|--select and fix an issue reported by coverity
Simplify code around _do_get_report_selection - remove "expected_idxs[]"
argument which is superfluous and add "allow_single" switch instead to
allow for recognition of "--configreport <report_name> -S" as well as
single "-S" if needed.

Null pointer dereferences  (FORWARD_NULL) /safe/guest2/covscan/LVM2.2.02.158/tools/reporter.c: 961 in _do_report_get_selection()
Null pointer dereferences  (FORWARD_NULL) Dereferencing null pointer "single_args".
2016-06-27 11:26:38 +02:00
Peter Rajnoha
82f4f073bb coverity: fix issues detected in recent code
Uninitialized variables  (UNINIT) /safe/guest2/covscan/LVM2.2.02.158/tools/toollib.c: 3520 in _process_pvs_in_vgs()
Uninitialized variables  (UNINIT) Using uninitialized value "do_report_ret_code".

Null pointer dereferences  (REVERSE_INULL) /safe/guest2/covscan/LVM2.2.02.158/libdm/libdm-report.c: 4745 in dm_report_output()
Null pointer dereferences  (REVERSE_INULL) Null-checking "rh" suggests that it may be null, but it has already been dereferenced on all paths leading to the check.

Incorrect expression  (MISSING_COMMA) /safe/guest2/covscan/LVM2.2.02.158/lib/log/log.c: 280 in _get_log_level_name()
Incorrect expression  (MISSING_COMMA) In the initialization of "log_level_names", a suspicious concatenated string ""noticeinfo"" is produced.

Null pointer dereferences  (FORWARD_NULL) /safe/guest2/covscan/LVM2.2.02.158/tools/reporter.c: 816 in_get_report_options()
Null pointer dereferences  (FORWARD_NULL) Comparing "mem" to null implies that "mem" might be null.
2016-06-27 11:22:29 +02:00
8 changed files with 2998 additions and 579 deletions

View File

@@ -1,5 +1,20 @@
Version 1.02.129 -
=================================
Update default dmstats field selections for groups.
Add 'obj_type', 'group_id', and 'statsname' fields to dmstats reports.
Add --area, --region, and --group to dmstats to control object selection.
Add --alias, --groupid, --regions to dmstats for group creation and deletion.
Add 'group' and 'ungroup' commands to dmstats.
Allow dm_stats_delete_group() to optionally delete all group members.
Add dm_stats_get_object_type() to return the type of object present.
Add dm_stats_walk_init() allowing control of objects visited by walks.
Add dm_stats_get_group_descriptor() to return the member list as a string.
Introduce dm_stats_get_nr_groups() and dm_stats_group_present().
Add dm_stats_{get,set}_alias() to set and retrieve alias names for groups.
Add dm_stats_get_group_id() to return the group ID for a given region.
Add dm_stats_{create,delete}_group() to allow grouping of stats regions.
Add enum-driven dm_stats_get_{metric,counter}() interfaces.
Add dm_bitset_parse_list() to parse a string representation of a bitset.
Thin dmeventd plugin umounts lvm2 volume only when pool is 95% or more.
Version 1.02.128 - 25th June 2016

View File

@@ -0,0 +1,14 @@
dm_bitset_parse_list
dm_stats_create_group
dm_stats_current_object_type
dm_stats_delete_group
dm_stats_get_alias
dm_stats_get_counter
dm_stats_get_group_descriptor
dm_stats_get_group_id
dm_stats_get_metric
dm_stats_get_nr_groups
dm_stats_group_present
dm_stats_object_type
dm_stats_set_alias
dm_stats_walk_init

View File

@@ -15,6 +15,8 @@
#include "dmlib.h"
#include <ctype.h>
/* FIXME: calculate this. */
#define INT_SHIFT 5
@@ -103,3 +105,99 @@ int dm_bit_get_first(dm_bitset_t bs)
{
return dm_bit_get_next(bs, -1);
}
/*
* Based on the Linux kernel __bitmap_parselist from lib/bitmap.c
*/
dm_bitset_t dm_bitset_parse_list(const char *str, struct dm_pool *mem)
{
unsigned a, b;
int c, old_c, totaldigits, ndigits, nmaskbits;
int at_start, in_range;
dm_bitset_t mask = NULL;
const char *start = str;
size_t len;
scan:
len = strlen(str);
totaldigits = c = 0;
nmaskbits = 0;
do {
at_start = 1;
in_range = 0;
a = b = 0;
ndigits = totaldigits;
/* Get the next value or range of values */
while (len) {
old_c = c;
c = *str++;
len--;
if (isspace(c))
continue;
/* A '\0' or a ',' signal the end of a value or range */
if (c == '\0' || c == ',')
break;
/*
* whitespaces between digits are not allowed,
* but it's ok if whitespaces are on head or tail.
* when old_c is whilespace,
* if totaldigits == ndigits, whitespace is on head.
* if whitespace is on tail, it should not run here.
* as c was ',' or '\0',
* the last code line has broken the current loop.
*/
if ((totaldigits != ndigits) && isspace(old_c))
goto_bad;
if (c == '-') {
if (at_start || in_range)
return_0;
b = 0;
in_range = 1;
at_start = 1;
continue;
}
if (!isdigit(c))
goto_bad;
b = b * 10 + (c - '0');
if (!in_range)
a = b;
at_start = 0;
totaldigits++;
}
if (ndigits == totaldigits)
continue;
/* if no digit is after '-', it's wrong */
if (at_start && in_range)
goto_bad;
if (!(a <= b))
goto_bad;
if (b >= nmaskbits)
nmaskbits = b + 1;
while ((a <= b) && mask) {
dm_bit_set(mask, a);
a++;
}
} while (len && c == ',');
if (!mask) {
if (!(mask = dm_bitset_create(mem, nmaskbits)))
goto_bad;
str = start;
goto scan;
}
return mask;
bad:
if (mask) {
if (mem)
dm_pool_free(mem, mask);
else
dm_bitset_destroy(mask);
}
return NULL;
}

View File

@@ -638,9 +638,24 @@ int dm_stats_populate(struct dm_stats *dms, const char *program_id,
* program creating the region. If program_id is NULL or the empty
* string the default program_id stored in the handle will be used.
*
* aux_data is an optional string argument passed to the kernel that is
* stored with the statistics region. It is not currently accessed by
* the library or kernel and may be used to store arbitrary user data.
* 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.
@@ -648,7 +663,7 @@ int dm_stats_populate(struct dm_stats *dms, const char *program_id,
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 *aux_data);
const char *program_id, const char *user_data);
/*
* Delete the specified statistics region. This will also mark the
@@ -714,6 +729,19 @@ void dm_stats_buffer_destroy(struct dm_stats *dms, char *buffer);
*/
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.
*/
@@ -732,6 +760,11 @@ uint64_t dm_stats_get_region_nr_areas(const struct dm_stats *dms,
*/
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.
@@ -904,51 +937,129 @@ 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 aux_data for a specific region. Only valid
* following a call to dm_stats_list(). The returned pointer does not
* need to be freed separately from the dm_stats handle but will become
* invalid after a dm_stats_destroy(), dm_stats_list(),
* dm_stats_populate(), or dm_stats_bind*() of the handle from which it
* was obtained.
* 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
* regions and areas that it stores. Iterators are provided to visit
* each region, or each area in a handle and accessor methods are
* provided to obtain properties and values for the region or area
* at the current cursor position.
* 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 areas when the
* region table is sparse (i.e. contains some present and some
* non-present region_id values either due to program_id filtering
* or the ordering of region creation and deletion).
* 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.
*/
/*
* Initialise the cursor of a dm_stats handle to address the first
* present region. It is valid to attempt to walk a NULL stats handle
* or a handle containing no present regions; in this case any call to
* dm_stats_walk_next() becomes a no-op and all calls to
* dm_stats_walk_end() return true.
* 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 0x1000000000000
#define DM_STATS_WALK_REGION 0x2000000000000
#define DM_STATS_WALK_GROUP 0x4000000000000
#define DM_STATS_WALK_ALL 0x7000000000000
#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 0x8000000000000
/*
* 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.
* 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);
/*
* Advance the statistics cursor to the next region.
* 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);
@@ -957,6 +1068,24 @@ void dm_stats_walk_next_region(struct dm_stats *dms);
*/
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
*
@@ -978,7 +1107,8 @@ int dm_stats_walk_end(struct dm_stats *dms);
* executed.
*/
#define dm_stats_foreach_region(dms) \
for (dm_stats_walk_start((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)))
/*
@@ -988,9 +1118,23 @@ for (dm_stats_walk_start((dms)); \
* be executed.
*/
#define dm_stats_foreach_area(dms) \
for (dm_stats_walk_start((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_group_walk_start(dms); \
!dm_stats_group_walk_end(dms); \
dm_stats_group_walk_next(dms))
/*
* Start a walk iterating over the regions contained in dm_stats handle
* 'dms'.
@@ -1074,11 +1218,71 @@ int dm_stats_get_current_area_len(const struct dm_stats *dms,
const char *dm_stats_get_current_region_program_id(const struct dm_stats *dms);
/*
* Return a pointer to the aux_data string for the region at the current
* cursor location.
* 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);
/*
* Call this to actually run the ioctl.
*/
@@ -1843,6 +2047,16 @@ int dm_bit_get_next(dm_bitset_t bs, int last_bit);
#define dm_bit_copy(bs1, bs2) \
memcpy((bs1) + 1, (bs2) + 1, ((*(bs1) / 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);
/* Returns number of set bits */
static inline unsigned hweight32(uint32_t i)
{
@@ -2732,6 +2946,12 @@ int dm_report_group_destroy(struct dm_report_group *group);
* 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:
*
@@ -2756,6 +2976,27 @@ int dm_report_group_destroy(struct dm_report_group *group);
#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);
@@ -2822,6 +3063,27 @@ uint64_t dm_stats_get_total_write_nsecs(const struct dm_stats *dms,
* 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);

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
.TH DMSTATS 8 "Jul 25 2015" "Linux" "MAINTENANCE COMMANDS"
.TH DMSTATS 8 "Jun 23 2016" "Linux" "MAINTENANCE COMMANDS"
.de OPT_PROGRAMS
. RB \%[ \-\-allprograms | \-\-programid
@@ -9,6 +9,11 @@
. RB \%[ \-\-allregions | \-\-regionid
. IR id ]
..
.de OPT_OBJECTS
. RB [ \-\-area ]
. RB [ \-\-region ]
. RB [ \-\-group ]
..
.
.\" Print units suffix, use with arg to print human
.\" man2html can't handle too many changes per command
@@ -87,8 +92,8 @@ dmstats \(em device-mapper statistics management
. IR start_sector
. BR \-\-length
. IR length | \fB\-\-segments ]
. RB \%[ \-\-auxdata
. IR data ]
. RB \%[ \-\-userdata
. IR user_data ]
. RB [ \-\-programid
. IR id ]
. ad b
@@ -110,6 +115,20 @@ dmstats \(em device-mapper statistics management
.
.HP
.B dmstats
.de CMD_GROUP
. ad l
. BR group
. RI [ device_name ]
. RB [ \-\-alias
. IR name ]
. RB [ \-\-alldevices ]
. RB [ \-\-regions
. IR regions ]
. ad b
..
.CMD_GROUP
.HP
.B dmstats
.de CMD_HELP
. ad l
. BR help
@@ -126,8 +145,11 @@ dmstats \(em device-mapper statistics management
. RI [ device_name ]
. RB [ \-\-histogram ]
. OPT_PROGRAMS
. RB [ \-\-statstype
. IR type_list ]
. RB [ \-\-units
. IR units ]
. OPT_OBJECTS
. RB \%[ \-\-nosuffix ]
. RB [ \-\-notimesuffix ]
. RB \%[ \-v | \-\-verbose [ \-v | \-\-verbose ]]
@@ -163,10 +185,13 @@ dmstats \(em device-mapper statistics management
. RB [ \-\-histogram ]
. OPT_PROGRAMS
. OPT_REGIONS
. OPT_OBJECTS
. RB [ \-O | \-\-sort
. IR sort_fields ]
. RB [ \-S | \-\-select
. IR selection ]
. RB [ \-\-statstype
. IR type_list ]
. RB [ \-\-units
. IR units ]
. RB [ \-\-nosuffix ]
@@ -174,6 +199,18 @@ dmstats \(em device-mapper statistics management
. ad b
..
.CMD_REPORT
.HP
.B dmstats
.de CMD_UNGROUP
. ad l
. BR ungroup
. RI [ device_name ]
. RB [ \-\-alldevices ]
. RB [ \-\-groupid
. IR id ]
. ad b
..
.CMD_UNGROUP
.
.PD
.ad b
@@ -199,6 +236,12 @@ commands require the use of \fB\-\-alldevices\fP when used in this way.
.SH OPTIONS
.
.HP
.BR \-\-alias
.IR name
.br
Specify an alias name for a group.
.
.HP
.BR \-\-alldevices
.br
If no device arguments are given allow operation on all devices when
@@ -216,6 +259,12 @@ Include all present regions for commands that normally accept a single
region identifier.
.
.HP
.BR \-\-area
.br
When peforming a list or report, include objects of type area in the
results.
.
.HP
.BR \-\-areas
.IR nr_areas
.br
@@ -232,12 +281,6 @@ optional suffix selects units of:
.HELP_UNITS
.
.HP
.BR \-\-auxdata
.IR aux_data
.br
Specify auxilliary data (a string) to be stored with a new region.
.
.HP
.BR \-\-clear
.br
When printing statistics counters, also atomically reset them to zero.
@@ -250,6 +293,18 @@ Specify the iteration count for repeating reports. If the count
argument is zero reports will continue to repeat until interrupted.
.
.HP
.BR \-\-group
.br
When peforming a list or report, include objects of type group in the
results.
.
.HP
.BR \-\-groupid
.IR id
.br
Specify the group to operate on.
.
.HP
.BR \-\-bounds
.IR histogram_boundaries \c
.RB [ ns | us | ms | s ]
@@ -340,12 +395,26 @@ program ID in order to select only regions with a matching value. The
default program ID for dmstats-managed regions is "dmstats".
.
.HP
.BR \-\-region
.br
When peforming a list or report, include objects of type region in the
results.
.
.HP
.BR \-\-regionid
.IR id
.br
Specify the region to operate on.
.
.HP
.BR \-\-regions
.IR region_list
.br
Specify a list of regions to group. The group list is a comma-separated
list of region identifiers. Continuous sequences of identifiers may be
expressed as a hyphen separated range, for example: '1-10'.
.
.HP
.BR \-\-relative
.br
If displaying the histogram report show relative (percentage) values
@@ -379,6 +448,20 @@ device. This causes a separate region to be allocated for each segment
of the device.
.
.HP
.BR \-\-statstype
.IR type_list
.br
Filter the types of statistics object included in a report or listing
according to the provided list. A report may include areas, regions,
and user-defined groups of regions that report aggregate data for all
group members.
The list may be a single object type, a comma separated list of types,
or the special value 'all'.
The currently available object types are 'area', 'region' and 'group'.
.
.HP
.BR \-\-units
.RI [ units ] \c
.RB [ h | H | \c
@@ -391,6 +474,15 @@ All sizes are output in these units:
Can also specify custom units e.g. \fB\-\-units\ 3M\fP.
.
.HP
.BR \-\-userdata
.IR user_data
.br
Specify user data (a word) to be stored with a new region. The value
is added to any internal auxilliary data (for example, group
information), and stored with the region in the aux_data field provided
by the kernel. Whitespace is not permitted.
.
.HP
.BR \-u | \-\-uuid
.br
Specify the uuid.
@@ -437,9 +529,9 @@ one milisecond can only be used when precise timestamps are enabled: if
\fB\-\-precise\fP is not given and values less than one milisecond are
used it will be enabled automatically.
An optional \fBprogram_id\fP or \fBaux_data\fP string may be associated
An optional \fBprogram_id\fP or \fBuser_data\fP string may be associated
with the region. A \fBprogram_id\fP may then be used to select regions
for subsequent list, print, and report operations. The \fBaux_data\fP
for subsequent list, print, and report operations. The \fBuser_data\fP
stores an arbitrary string and is not used by dmstats or the
device-mapper kernel statistics subsystem.
@@ -461,6 +553,28 @@ All regions registered on a device may be removed using
To remove all regions on all devices both \fB\-\-allregions\fP and
\fB\-\-alldevices\fP must be used.
If a \fB\-\-groupid\fP is given instead of a \fB\-\-regionid\fP the
command will attempt to delete the group and all regions that it
contains.
If a deleted region is the first member of a group of regions the group
will also be removed.
.
.HP
.CMD_GROUP
.br
Combine one or more statistics regions on the specified device into a
group.
The list of regions to be grouped is specified with \fB\-\-regions\fP
and an optional alias may be assigned with \fB\-\-alias\fP. The set of
regions is given as a comma-separated list of region identifiers. A
continuous range of identifers spanning from \fBR1\fP to \fBR2\fP may
be expressed as '\fBR1\fP-\fBR2\fP'.
On success the group list and newly created \fBgroup_id\fP are
printed to stdout.
.
.HP
.CMD_HELP
@@ -471,16 +585,23 @@ the list of report fields.
.HP
.CMD_LIST
.br
List the statistics regions registered on the device. If the
\fB\-\-allprograms\fP switch is given all regions will be listed
List the statistics regions, areas, or groups registered on the device.
If the \fB\-\-allprograms\fP switch is given all regions will be listed
regardless of region program ID values.
If \fB\-v\fP or \fB\-\-verbose\fP is given the report will include
a row of information for each area contained in each region displayed.
By default only regions and groups are included in list output. If
\fB\-v\fP or \fB\-\-verbose\fP is given the report will also include a
row of information for each configured group and for each area contained
in each region displayed (regions that contain a single area are by
default omitted from the verbose list since their properties are
identical to the area that they contain - to view all regions regardless
of the number of areas they contain use \fB\-\-statstype\fP).
Specific combinations of objects may be selected using the
\fB\-\-statstype\fP option.
If \fB\-\-histogram\fP is given the report will include the bin count
and latency boundary values for any configured histograms.
.
.HP
.CMD_PRINT
.br
@@ -490,7 +611,7 @@ present regions.
.HP
.CMD_REPORT
.br
Start a report for the specified region or for all present regions. If
Start a report for the specified object or for all present objects. If
the count argument is specified, the report will repeat at a fixed
interval set by the \fB\-\-interval\fP option. The default interval is
one second.
@@ -503,19 +624,41 @@ values and latency boundaries.
If the \fB\-\-relative\fP is used the default histogram field displays
bin values as a percentage of the total number of I/Os.
Object types (areas, regions and groups) are selected using the
\fB\-\-statstype\fP option.
.
.SH REGIONS AND AREAS
.HP
.CMD_UNGROUP
.br
Remove an existing group and return all the group's regions to their
original state.
The group to be removed is specified using \fB\-\-groupid\fP.
.
.SH REGIONS, AREAS, AND GROUPS
.
The device-mapper statistics facility allows separate performance
counters to be maintained for arbitrary regions of devices. A region may
span any range: from a single sector to the whole device. A region may
be further sub-divided into a number of distinct areas (one or more),
each with its own counter set.
each with its own counter set. In this case a summary value for the
entire region is also available for use in reports.
In addition, one or more regions on one device can be combined into
a statistics group allowing reporting of aggregate values for all
regions and areas making up the group.
By default new regions span the entire device. The \fB\-\-start\fP and
\fB\-\-length\fP options allows a region of any size to be placed at any
location on the device.
Using offsets it is possible to create regions that map individual
objects within a block device (for example: partitions, files in a file
system, or stripes or other structures in a RAID volume). Groups allow
several non-contiguous regions to be assembled together for reporting
and data aggregation.
A region may be either divided into the specified number of equal-sized
areas, or into areas of the given size by specifying one of
\fB\-\-areas\fP or \fB\-\-areasize\fP when creating a region with the
@@ -531,6 +674,19 @@ values).
Depending on the sequence of create and delete operations, gaps may
exist in the sequence of \fBregion_id\fP values for a particular device.
The \fBregion_id\fP should be treated as an opaque identifier used to
reference the region.
.
.P
.B Group identifiers
.P
Groups are also assigned an integer identifier at creation time;
like region identifiers, group identifiers are unique within the
containing device.
The \fBgroup_id\fP should be treated as an opaque identifier used to
reference the group.
.
.SH REPORT FIELDS
.
@@ -591,12 +747,12 @@ Percentage of CPU time during which I/O requests were issued to the
device (bandwidth utilization for the device). Device saturation occurs
when this value is close to 100%.
.
.SS Region and area meta fields
.SS Group, region and area meta fields
.
Meta fields provide information about the region or area that the
statistics values relate to. This includes the region and area
Meta fields provide information about the groups, regions, or areas that
the statistics values relate to. This includes the region and area
identifier, start, length, and counts, as well as the program ID and
auxiliary data values.
user data values.
.TP
.B region_id
Region identifier. This is a non-negative integer returned by the kernel
@@ -633,8 +789,12 @@ The number of areas in this region.
.B program_id
The program ID value associated with this region.
.TP
.B aux_data
The auxiliary data value associated with this region.
.B user_data
The user data value associated with this region.
.TP
.B group_id
Group identifier. This is a non-negative integer returned by the dmstats
\fBgroup\fP command when a statistics group is created.
.TP
.B interval_ns
The estimated interval over which the current counter values have

View File

@@ -156,12 +156,13 @@ enum {
READ_ONLY = 0,
ADD_NODE_ON_CREATE_ARG,
ADD_NODE_ON_RESUME_ARG,
ALIAS_ARG,
ALL_DEVICES_ARG,
ALL_PROGRAMS_ARG,
ALL_REGIONS_ARG,
AREA_ARG,
AREAS_ARG,
AREA_SIZE_ARG,
AUX_DATA_ARG,
BOUNDS_ARG,
CHECKS_ARG,
CLEAR_ARG,
@@ -172,6 +173,8 @@ enum {
EXEC_ARG,
FORCE_ARG,
GID_ARG,
GROUP_ARG,
GROUP_ID_ARG,
HELP_ARG,
HISTOGRAM_ARG,
INACTIVE_ARG,
@@ -179,6 +182,7 @@ enum {
LENGTH_ARG,
MANGLENAME_ARG,
MAJOR_ARG,
REGIONS_ARG,
MINOR_ARG,
MODE_ARG,
NAMEPREFIXES_ARG,
@@ -197,6 +201,7 @@ enum {
PROGRAM_ID_ARG,
RAW_ARG,
READAHEAD_ARG,
REGION_ARG,
REGION_ID_ARG,
RELATIVE_ARG,
RETRY_ARG,
@@ -214,6 +219,7 @@ enum {
UNBUFFERED_ARG,
UNITS_ARG,
UNQUOTED_ARG,
USER_DATA_ARG,
UUID_ARG,
VERBOSE_ARG,
VERIFYUDEV_ARG,
@@ -261,7 +267,16 @@ static struct dm_timestamp *_initial_timestamp = NULL;
static uint64_t _disp_factor = 512; /* display sizes in sectors */
static char _disp_units = 's';
const char *_program_id = DM_STATS_PROGRAM_ID; /* program_id used for reports. */
static int _stats_report_by_areas = 1; /* output per-area info for stats reports. */
static uint64_t _statstype = 0; /* stats objects to report */
/* string names for stats object types */
const char *_stats_types[] = {
"all",
"area",
"region",
"group",
NULL
};
/* report timekeeping */
static struct dm_timestamp *_cycle_timestamp = NULL;
@@ -801,7 +816,7 @@ out:
static int _display_info_cols(struct dm_task *dmt, struct dm_info *info)
{
struct dmsetup_report_obj obj;
uint64_t walk_flags = _statstype;
int r = 0;
if (!info->exists) {
@@ -832,6 +847,13 @@ static int _display_info_cols(struct dm_task *dmt, struct dm_info *info)
dm_task_get_name(dmt), '-')))
goto_out;
if (!(_report_type & (DR_STATS | DR_STATS_META))) {
if (!dm_report_object(_report, &obj))
goto_out;
r = 1;
goto out;
}
/*
* Obtain statistics for the current reporting object and set
* the interval estimate used for stats rate conversion.
@@ -851,10 +873,9 @@ static int _display_info_cols(struct dm_task *dmt, struct dm_info *info)
log_debug("Adjusted sample interval duration: %12"PRIu64"ns", _last_interval);
/* use measured approximation for calculations */
dm_stats_set_sampling_interval_ns(obj.stats, _last_interval);
}
/* Only a dm_stats_list is needed for DR_STATS_META reports. */
if (!obj.stats && (_report_type & DR_STATS_META)) {
} else if (!obj.stats && (_report_type & DR_STATS_META)
/* Only a dm_stats_list is needed for DR_STATS_META reports. */
&& !(_report_type & DR_STATS)) {
if (!(obj.stats = dm_stats_create(DM_STATS_PROGRAM_ID)))
goto_out;
@@ -863,25 +884,23 @@ static int _display_info_cols(struct dm_task *dmt, struct dm_info *info)
if (!dm_stats_list(obj.stats, _program_id))
goto_out;
/* No regions to report */
/* No regions to report is not an error */
if (!dm_stats_get_nr_regions(obj.stats))
goto_out;
goto out;
}
/*
* Walk any statistics regions contained in the current
* reporting object: for objects with a NULL stats handle,
* or a handle containing no registered regions, this loop
* always executes exactly once.
*/
/* group report with no groups? */
if ((walk_flags == DM_STATS_WALK_GROUP)
&& !dm_stats_get_nr_groups(obj.stats))
goto out;
dm_stats_walk_init(obj.stats, walk_flags);
dm_stats_walk_do(obj.stats) {
if (!dm_report_object(_report, &obj))
goto_out;
if (_stats_report_by_areas)
dm_stats_walk_next(obj.stats);
else
dm_stats_walk_next_region(obj.stats);
dm_stats_walk_next(obj.stats);
} dm_stats_walk_while(obj.stats);
r = 1;
out:
@@ -3247,7 +3266,19 @@ static int _dm_stats_region_id_disp(struct dm_report *rh,
void *private __attribute__((unused)))
{
const struct dm_stats *dms = (const struct dm_stats *) data;
uint64_t region_id = dm_stats_get_current_region(dms);
uint64_t group_id, region_id = dm_stats_get_current_region(dms);
char *group_buf = NULL, *repstr;
if (dm_stats_current_object_type(dms) == DM_STATS_OBJECT_TYPE_GROUP) {
group_id = dm_stats_get_group_id(dms, dm_stats_get_current_region(dms));
if (!dm_stats_get_group_descriptor(dms, group_id, &group_buf))
return 0;
/* group_buf will disappear with the current handle */
repstr = dm_pool_strdup(mem, group_buf);
dm_report_field_set_value(field, repstr, &group_id);
return 1;
}
return dm_report_field_uint64(rh, field, &region_id);
}
@@ -3314,6 +3345,10 @@ static int _dm_stats_area_id_disp(struct dm_report *rh,
{
const struct dm_stats *dms = (const struct dm_stats *) data;
uint64_t area_id = dm_stats_get_current_area(dms);
if (dm_stats_current_object_type(dms) == DM_STATS_OBJECT_TYPE_GROUP)
area_id = 0;
return dm_report_field_uint64(rh, field, &area_id);
}
@@ -3416,6 +3451,25 @@ static int _dm_stats_area_count_disp(struct dm_report *rh,
return dm_report_field_uint64(rh, field, &area_count);
}
static int _dm_stats_group_id_disp(struct dm_report *rh,
struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field, const void *data,
void *private __attribute__((unused)))
{
const struct dm_stats *dms = (const struct dm_stats *) data;
uint64_t group_id;
group_id = dm_stats_get_group_id(dms,
dm_stats_get_current_region(dms));
if (!dm_stats_group_present(dms, group_id)) {
dm_report_field_set_value(field, "-", &group_id);
return 1;
}
return dm_report_field_uint64(rh, field, &group_id);
}
static int _dm_stats_program_id_disp(struct dm_report *rh,
struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field, const void *data,
@@ -3428,16 +3482,40 @@ static int _dm_stats_program_id_disp(struct dm_report *rh,
return dm_report_field_string(rh, field, (const char * const *) &program_id);
}
static int _dm_stats_aux_data_disp(struct dm_report *rh,
static int _dm_stats_user_data_disp(struct dm_report *rh,
struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field, const void *data,
void *private __attribute__((unused)))
{
const struct dm_stats *dms = (const struct dm_stats *) data;
const char *user_data;
if (!(user_data = dm_stats_get_current_region_aux_data(dms)))
return_0;
return dm_report_field_string(rh, field, (const char * const *) &user_data);
}
static int _dm_stats_name_disp(struct dm_report *rh,
struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field, const void *data,
void *private __attribute__((unused)))
{
const struct dm_stats *dms = (const struct dm_stats *) data;
const char *stats_name;
if (!(stats_name = dm_stats_get_alias(dms, DM_STATS_REGION_CURRENT)))
return_0;
return dm_report_field_string(rh, field, (const char * const *) &stats_name);
}
static int _dm_stats_object_type_disp(struct dm_report *rh,
struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field, const void *data,
void *private __attribute__((unused)))
{
const struct dm_stats *dms = (const struct dm_stats *) data;
const char *aux_data;
if (!(aux_data = dm_stats_get_current_region_aux_data(dms)))
return_0;
return dm_report_field_string(rh, field, (const char * const *) &aux_data);
int type = dm_stats_current_object_type(dms);
return dm_report_field_string(rh, field, (const char * const *) &_stats_types[type]);
}
static int _dm_stats_precise_disp(struct dm_report *rh,
@@ -4200,12 +4278,15 @@ FIELD_F(STATS_META, SIZ, "ArStart", 7, dm_stats_area_start, "area_start", "Area
FIELD_F(STATS_META, SIZ, "ArSize", 6, dm_stats_area_len, "area_len", "Area length.")
FIELD_F(STATS_META, SIZ, "ArOff", 5, dm_stats_area_offset, "area_offset", "Area offset from start of region.")
FIELD_F(STATS_META, NUM, "#Areas", 6, dm_stats_area_count, "area_count", "Area count.")
FIELD_F(STATS_META, NUM, "GrpID", 5, dm_stats_group_id, "group_id", "Group ID.")
FIELD_F(STATS_META, STR, "ProgID", 6, dm_stats_program_id, "program_id", "Program ID.")
FIELD_F(STATS_META, STR, "AuxDat", 6, dm_stats_aux_data, "aux_data", "Auxiliary data.")
FIELD_F(STATS_META, STR, "UserData", 8, dm_stats_user_data, "user_data", "Auxiliary data.")
FIELD_F(STATS_META, STR, "Precise", 7, dm_stats_precise, "precise", "Set if nanosecond precision counters are enabled.")
FIELD_F(STATS_META, STR, "#Bins", 9, dm_stats_hist_bins, "hist_bins", "The number of histogram bins configured.")
FIELD_F(STATS_META, STR, "Histogram Bounds", 16, dm_stats_hist_bounds, "hist_bounds", "Latency histogram bin boundaries.")
FIELD_F(STATS_META, STR, "Histogram Ranges", 16, dm_stats_hist_ranges, "hist_ranges", "Latency histogram bin ranges.")
FIELD_F(STATS_META, STR, "Name", 16, dm_stats_name, "stats_name", "Stats name of current object.")
FIELD_F(STATS_META, STR, "ObjType", 11, dm_stats_object_type, "obj_type", "Type of stats object being reported.")
{0, 0, 0, 0, "", "", NULL, NULL},
/* *INDENT-ON* */
};
@@ -4233,7 +4314,7 @@ static const char *splitname_report_options = "vg_name,lv_name,lv_layer";
"await,read_await,write_await"
/* Device, region and area metadata. */
#define STATS_DEV_INFO "name,region_id"
#define STATS_DEV_INFO "statsname,group_id,region_id,obj_type"
#define STATS_AREA_INFO "area_id,area_start,area_len"
#define STATS_AREA_INFO_FULL STATS_DEV_INFO ",region_start,region_len,area_count,area_id,area_start,area_len"
#define STATS_REGION_INFO STATS_DEV_INFO ",region_start,region_len,area_count,area_len"
@@ -4530,6 +4611,22 @@ static int _bind_stats_device(struct dm_stats *dms, const char *name)
return 1;
}
static int _stats_clear_one_region(struct dm_stats *dms, uint64_t region_id)
{
if (!dm_stats_region_present(dms, region_id)) {
log_error("No such region: %"PRIu64".", region_id);
return 0;
}
if (!dm_stats_clear_region(dms, region_id)) {
log_error("Clearing statistics region %"PRIu64" failed.",
region_id);
return 0;
}
log_info("Cleared statistics region %"PRIu64".", region_id);
return 1;
}
static int _stats_clear_regions(struct dm_stats *dms, uint64_t region_id)
{
int allregions = (region_id == DM_STATS_REGIONS_ALL);
@@ -4540,22 +4637,14 @@ static int _stats_clear_regions(struct dm_stats *dms, uint64_t region_id)
if (!dm_stats_get_nr_regions(dms))
return 1;
dm_stats_walk_do(dms) {
if (allregions)
region_id = dm_stats_get_current_region(dms);
if (!allregions)
return _stats_clear_one_region(dms, region_id);
if (!dm_stats_region_present(dms, region_id)) {
log_error("No such region: %"PRIu64".", region_id);
return 0;
}
if (!dm_stats_clear_region(dms, region_id)) {
log_error("Clearing statistics region %"PRIu64" failed.",
region_id);
return 0;
}
log_info("Cleared statistics region %"PRIu64".", region_id);
dm_stats_walk_next_region(dms);
} dm_stats_walk_while(dms);
dm_stats_foreach_region(dms) {
region_id = dm_stats_get_current_region(dms);
if (!_stats_clear_one_region(dms, region_id))
return_0;
}
return 1;
}
@@ -4671,7 +4760,7 @@ static int _do_stats_create_regions(struct dm_stats *dms,
uint64_t len, int64_t step,
int segments,
const char *program_id,
const char *aux_data)
const char *user_data)
{
uint64_t this_start = 0, this_len = len, region_id = UINT64_C(0);
const char *devname = NULL, *histogram = _string_args[BOUNDS_ARG];
@@ -4732,7 +4821,7 @@ static int _do_stats_create_regions(struct dm_stats *dms,
if (!dm_stats_create_region(dms, &region_id,
this_start, this_len, step,
precise, bounds,
program_id, aux_data)) {
program_id, user_data)) {
log_error("%s: Could not create statistics region.",
devname);
goto out;
@@ -4755,7 +4844,7 @@ out:
static int _stats_create(CMD_ARGS)
{
struct dm_stats *dms;
const char *name, *aux_data = "", *program_id = DM_STATS_PROGRAM_ID;
const char *name, *user_data = "", *program_id = DM_STATS_PROGRAM_ID;
uint64_t start = 0, len = 0, areas = 0, area_size = 0;
int64_t step = 0;
@@ -4836,8 +4925,8 @@ static int _stats_create(CMD_ARGS)
if (!strlen(program_id) && !_switches[FORCE_ARG])
program_id = DM_STATS_PROGRAM_ID;
if (_switches[AUX_DATA_ARG])
aux_data = _string_args[AUX_DATA_ARG];
if (_switches[USER_DATA_ARG])
user_data = _string_args[USER_DATA_ARG];
if (!(dms = dm_stats_create(DM_STATS_PROGRAM_ID)))
return_0;
@@ -4867,7 +4956,7 @@ static int _stats_create(CMD_ARGS)
return _do_stats_create_regions(dms, name, start, len, step,
_switches[SEGMENTS_ARG],
program_id, aux_data);
program_id, user_data);
bad:
dm_stats_destroy(dms);
@@ -4877,7 +4966,7 @@ bad:
static int _stats_delete(CMD_ARGS)
{
struct dm_stats *dms;
uint64_t region_id;
uint64_t region_id, group_id;
char *name = NULL;
const char *program_id = DM_STATS_PROGRAM_ID;
int allregions = _switches[ALL_REGIONS_ARG];
@@ -4889,8 +4978,13 @@ static int _stats_delete(CMD_ARGS)
_report = NULL;
}
if (!_switches[REGION_ID_ARG] && !allregions) {
err("Please specify a --regionid or use --allregions.");
if (_switches[REGION_ID_ARG] && _switches[GROUP_ID_ARG]) {
err("Please use one of --regionid and --groupid.");
return 0;
}
if (!_switches[REGION_ID_ARG] && !allregions && !_switches[GROUP_ID_ARG]) {
err("Please specify a --regionid or --groupid, or use --allregions.");
return 0;
}
@@ -4912,6 +5006,7 @@ static int _stats_delete(CMD_ARGS)
program_id = DM_STATS_ALL_PROGRAMS;
region_id = (uint64_t) _int_args[REGION_ID_ARG];
group_id = (uint64_t) _int_args[GROUP_ID_ARG];
if (!(dms = dm_stats_create(program_id)))
return_0;
@@ -4919,7 +5014,9 @@ static int _stats_delete(CMD_ARGS)
if (!_bind_stats_device(dms, name))
goto_out;
if (allregions && !dm_stats_list(dms, program_id))
/* allregions and group delete require a listed handle */
if ((allregions || _switches[GROUP_ID_ARG])
&& !dm_stats_list(dms, program_id))
goto_out;
if (allregions && !dm_stats_get_nr_regions(dms)) {
@@ -4928,16 +5025,24 @@ static int _stats_delete(CMD_ARGS)
goto out;
}
dm_stats_walk_do(dms) {
if (_switches[ALL_REGIONS_ARG])
region_id = dm_stats_get_current_region(dms);
if (!dm_stats_delete_region(dms, region_id)) {
log_error("Could not delete statistics region.");
if (_switches[GROUP_ID_ARG]) {
if (!dm_stats_delete_group(dms, group_id, 1)) {
log_error("Could not delete statistics group.");
goto out;
}
log_info("Deleted statistics region %" PRIu64, region_id);
dm_stats_walk_next_region(dms);
} dm_stats_walk_while(dms);
} else if (_switches[ALL_REGIONS_ARG]) {
dm_stats_foreach_region(dms) {
region_id = dm_stats_get_current_region(dms);
if (!dm_stats_delete_region(dms, region_id)) {
log_error("Could not delete statistics region.");
goto out;
}
log_info("Deleted statistics region %" PRIu64, region_id);
}
} else {
dm_stats_delete_region(dms, region_id);
log_info("Deleted statistics region " FMTu64 ".\n", region_id);
}
r = 1;
@@ -4946,6 +5051,23 @@ out:
return r;
}
static int _stats_print_one_region(struct dm_stats *dms, int clear,
uint64_t region_id)
{
char *stbuff = NULL;
/*FIXME: line control for large regions */
if (!(stbuff = dm_stats_print_region(dms, region_id, 0, 0, clear))) {
log_error("Could not print statistics region.");
return 0;
}
printf("%s", stbuff);
dm_stats_buffer_destroy(dms, stbuff);
return 1;
}
static int _stats_print(CMD_ARGS)
{
struct dm_stats *dms;
@@ -4974,8 +5096,6 @@ static int _stats_print(CMD_ARGS)
name = argv[0];
}
region_id = (uint64_t) _int_args[REGION_ID_ARG];
if (!(dms = dm_stats_create(DM_STATS_PROGRAM_ID)))
return_0;
@@ -4990,14 +5110,18 @@ static int _stats_print(CMD_ARGS)
goto out;
}
dm_stats_walk_do(dms) {
if (_switches[ALL_REGIONS_ARG])
region_id = dm_stats_get_current_region(dms);
if (!allregions) {
region_id = (uint64_t) _int_args[REGION_ID_ARG];
if (!_stats_print_one_region(dms, clear, region_id))
goto_out;
r = 1;
goto out;
}
if (!dm_stats_region_present(dms, region_id)) {
log_error("No such region: %"PRIu64".", region_id);
goto out;
}
dm_stats_foreach_region(dms) {
region_id = dm_stats_get_current_region(dms);
if (!_stats_print_one_region(dms, clear, region_id))
goto_out;
/*FIXME: line control for large regions */
if (!(stbuff = dm_stats_print_region(dms, region_id, 0, 0, clear))) {
@@ -5006,11 +5130,8 @@ static int _stats_print(CMD_ARGS)
}
printf("%s", stbuff);
dm_stats_buffer_destroy(dms, stbuff);
dm_stats_walk_next_region(dms);
} dm_stats_walk_while(dms);
}
r = 1;
@@ -5021,19 +5142,30 @@ out:
static int _stats_report(CMD_ARGS)
{
int r = 0;
int r = 0, objtype_args;
struct dm_task *dmt;
char *name = NULL;
objtype_args = (_switches[AREA_ARG]
|| _switches[REGION_ARG]
|| _switches[GROUP_ARG]);
if (_switches[PROGRAM_ID_ARG])
_program_id = _string_args[PROGRAM_ID_ARG];
if (_switches[ALL_PROGRAMS_ARG])
_program_id = "";
if (!_switches[VERBOSE_ARG] && !strcmp(subcommand, "list"))
_stats_report_by_areas = 0;
if (_switches[VERBOSE_ARG] && !strcmp(subcommand, "list"))
_statstype |= (DM_STATS_WALK_ALL
| DM_STATS_WALK_SKIP_SINGLE_AREA);
/* suppress duplicates unless the user has requested all regions */
if (!strcmp(subcommand, "report") && !objtype_args)
/* suppress duplicate rows of output */
_statstype |= (DM_STATS_WALK_ALL
| DM_STATS_WALK_SKIP_SINGLE_AREA);
if (names)
name = names->name;
@@ -5068,6 +5200,121 @@ out:
return r;
}
static int _stats_group(CMD_ARGS)
{
char *name, *alias = NULL, *regions = NULL;
struct dm_stats *dms;
uint64_t group_id;
int r = 0;
/* group does not use a report */
if (_report) {
dm_report_free(_report);
_report = NULL;
}
if (!_switches[REGIONS_ARG]) {
err("Group requires --regions.");
return 0;
}
regions = _string_args[REGIONS_ARG];
if (names)
name = names->name;
else {
if (!argc && !_switches[UUID_ARG] && !_switches[MAJOR_ARG]) {
if (!_switches[ALL_DEVICES_ARG]) {
log_error("Please specify device(s) or use "
"--alldevices.");
return 0;
}
return _process_all(cmd, subcommand, argc, argv, 0, _stats_group);
}
name = argv[0];
}
if (_switches[ALIAS_ARG])
alias = _string_args[ALIAS_ARG];
if (!(dms = dm_stats_create(DM_STATS_PROGRAM_ID)))
return_0;
if (!_bind_stats_device(dms, name))
goto_out;
if (!dm_stats_list(dms, NULL))
goto_out;
if(!dm_stats_create_group(dms, regions, alias, &group_id)) {
log_error("Could not create group on %s: %s", name, regions);
goto out;
}
printf("Grouped regions %s as group ID " FMTu64 " on %s\n",
regions, group_id, name);
r = 1;
out:
dm_stats_destroy(dms);
return r;
}
static int _stats_ungroup(CMD_ARGS)
{
struct dm_stats *dms;
uint64_t group_id;
char *name;
int r = 0;
/* ungroup does not use a report */
if (_report) {
dm_report_free(_report);
_report = NULL;
}
if (!_switches[GROUP_ID_ARG]) {
err("Please specify group id.");
return 0;
}
group_id = (uint64_t) _int_args[GROUP_ID_ARG];
if (names)
name = names->name;
else {
if (!argc && !_switches[UUID_ARG] && !_switches[MAJOR_ARG]) {
if (!_switches[ALL_DEVICES_ARG]) {
log_error("Please specify device(s) or use "
"--alldevices.");
return 0;
}
return _process_all(cmd, subcommand, argc, argv, 0, _stats_ungroup);
}
name = argv[0];
}
if (!(dms = dm_stats_create(DM_STATS_PROGRAM_ID)))
return_0;
if (!_bind_stats_device(dms, name))
goto_out;
if (!dm_stats_list(dms, NULL))
goto_out;
if (!(r = dm_stats_delete_group(dms, group_id, 0)))
log_error("Could not delete group " FMTu64 " on %s.",
group_id, name);
printf("Removed group ID "FMTu64" on %s\n", group_id, name);
out:
dm_stats_destroy(dms);
return r;
}
/*
* Command dispatch tables and usage.
*/
@@ -5080,30 +5327,35 @@ static int _stats_help(CMD_ARGS);
* clear [--regionid id] <device_name>
* create [--areas nr_areas] [--areasize size]
* [ [--start start] [--length len] | [--segments]]
* [--auxdata data] [--programid id] [<device_name>]
* [--userdata data] [--programid id] [<device_name>]
* delete [--regionid] <device_name>
* delete_all [--programid id]
* group [--alias name] [--alldevices] [--regions <regions>] [<device_name>]
* list [--programid id] [<device_name>]
* print [--clear] [--programid id] [--regionid id] [<device_name>]
* report [--interval seconds] [--count count] [--units units] [--regionid id]
* [--programid id] [<device>]
* ungroup [--alldevices] [--groupid id] [<device_name>]
*/
#define AREA_OPTS "[--areas <nr_areas>] [--areasize <size>] "
#define CREATE_OPTS "[--start <start> [--length <len>]]\n\t\t" AREA_OPTS
#define ID_OPTS "[--programid <id>] [--auxdata <data> ] "
#define ID_OPTS "[--programid <id>] [--userdata <data> ] "
#define SELECT_OPTS "[--programid <id>] [--regionid <id>] "
#define PRINT_OPTS "[--clear] " SELECT_OPTS
#define REPORT_OPTS "[--interval <seconds>] [--count <cnt>]\n\t\t[--units <u>]" SELECT_OPTS
#define GROUP_OPTS "[--alias NAME] --regions <regions>"
static struct command _stats_subcommands[] = {
{"help", "", 0, 0, 0, 0, _stats_help},
{"clear", "--regionid <id> [<device>]", 0, -1, 1, 0, _stats_clear},
{"create", CREATE_OPTS "\n\t\t" ID_OPTS "[<device>]", 0, -1, 1, 0, _stats_create},
{"delete", "--regionid <id> <device>", 1, -1, 1, 0, _stats_delete},
{"group", GROUP_OPTS, 1, -1, 1, 0, _stats_group},
{"list", "[--programid <id>] [<device>]", 0, -1, 1, 0, _stats_report},
{"print", PRINT_OPTS "[<device>]", 0, -1, 1, 0, _stats_print},
{"report", REPORT_OPTS "[<device>]", 0, -1, 1, 0, _stats_report},
{"ungroup", "--groupid <id> [device]", 1, -1, 1, 0, _stats_ungroup},
{"version", "", 0, -1, 1, 0, _version},
{NULL, NULL, 0, 0, 0, 0, NULL}
};
@@ -5177,7 +5429,7 @@ static void _stats_usage(FILE *out)
fprintf(out, " [-h|--help]\n");
fprintf(out, " [-v|--verbose [-v|--verbose ...]]\n");
fprintf(out, " [--areas <nr_areas>] [--areasize <size>]\n");
fprintf(out, " [--auxdata <data>] [--clear]\n");
fprintf(out, " [--userdata <data>] [--clear]\n");
fprintf(out, " [--count <count>] [--interval <seconds>]\n");
fprintf(out, " [-o <fields>] [-O|--sort <sort_fields>]\n");
fprintf(out, " [--programid <id>]\n");
@@ -5319,6 +5571,18 @@ static int _stats(CMD_ARGS)
{
const struct command *stats_cmd;
if (_switches[AREA_ARG] || _switches[REGION_ARG] || _switches[GROUP_ARG])
_statstype = 0; /* switches will OR flags in */
else
_statstype = DM_STATS_WALK_REGION | DM_STATS_WALK_GROUP;
if (_switches[AREA_ARG])
_statstype |= DM_STATS_WALK_AREA;
if (_switches[REGION_ARG])
_statstype |= DM_STATS_WALK_REGION;
if (_switches[GROUP_ARG])
_statstype |= DM_STATS_WALK_GROUP;
if (!(stats_cmd = _find_stats_subcommand(subcommand))) {
log_error("Unknown stats command.");
_stats_help(stats_cmd, NULL, argc, argv, NULL, multiple_devices);
@@ -5702,12 +5966,13 @@ static int _process_switches(int *argcp, char ***argvp, const char *dev_dir)
#ifdef HAVE_GETOPTLONG
static struct option long_options[] = {
{"readonly", 0, &ind, READ_ONLY},
{"alias", 1, &ind, ALIAS_ARG},
{"alldevices", 0, &ind, ALL_DEVICES_ARG},
{"allprograms", 0, &ind, ALL_PROGRAMS_ARG},
{"allregions", 0, &ind, ALL_REGIONS_ARG},
{"area", 0, &ind, AREA_ARG},
{"areas", 1, &ind, AREAS_ARG},
{"areasize", 1, &ind, AREA_SIZE_ARG},
{"auxdata", 1, &ind, AUX_DATA_ARG},
{"bounds", 1, &ind, BOUNDS_ARG},
{"checks", 0, &ind, CHECKS_ARG},
{"clear", 0, &ind, CLEAR_ARG},
@@ -5718,6 +5983,8 @@ static int _process_switches(int *argcp, char ***argvp, const char *dev_dir)
{"exec", 1, &ind, EXEC_ARG},
{"force", 0, &ind, FORCE_ARG},
{"gid", 1, &ind, GID_ARG},
{"group", 0, &ind, GROUP_ARG},
{"groupid", 1, &ind, GROUP_ID_ARG},
{"help", 0, &ind, HELP_ARG},
{"histogram", 0, &ind, HISTOGRAM_ARG},
{"inactive", 0, &ind, INACTIVE_ARG},
@@ -5743,6 +6010,8 @@ static int _process_switches(int *argcp, char ***argvp, const char *dev_dir)
{"programid", 1, &ind, PROGRAM_ID_ARG},
{"raw", 0, &ind, RAW_ARG},
{"readahead", 1, &ind, READAHEAD_ARG},
{"region", 0, &ind, REGION_ARG},
{"regions", 1, &ind, REGIONS_ARG},
{"regionid", 1, &ind, REGION_ID_ARG},
{"relative", 0, &ind, RELATIVE_ARG},
{"retry", 0, &ind, RETRY_ARG},
@@ -5761,6 +6030,7 @@ static int _process_switches(int *argcp, char ***argvp, const char *dev_dir)
{"uuid", 1, &ind, UUID_ARG},
{"unbuffered", 0, &ind, UNBUFFERED_ARG},
{"unquoted", 0, &ind, UNQUOTED_ARG},
{"userdata", 1, &ind, USER_DATA_ARG},
{"verbose", 1, &ind, VERBOSE_ARG},
{"verifyudev", 0, &ind, VERIFYUDEV_ARG},
{"version", 0, &ind, VERSION_ARG},
@@ -5838,12 +6108,18 @@ static int _process_switches(int *argcp, char ***argvp, const char *dev_dir)
optind = OPTIND_INIT;
while ((ind = -1, c = GETOPTLONG_FN(*argcp, *argvp, "cCfG:hj:m:M:no:O:rS:u:U:vy",
long_options, NULL)) != -1) {
if (ind == ALIAS_ARG) {
_switches[ALIAS_ARG]++;
_string_args[ALIAS_ARG] = optarg;
}
if (ind == ALL_DEVICES_ARG)
_switches[ALL_DEVICES_ARG]++;
if (ind == ALL_PROGRAMS_ARG)
_switches[ALL_PROGRAMS_ARG]++;
if (ind == ALL_REGIONS_ARG)
_switches[ALL_REGIONS_ARG]++;
if (ind == AREA_ARG)
_switches[AREA_ARG]++;
if (ind == AREAS_ARG) {
_switches[AREAS_ARG]++;
_int_args[AREAS_ARG] = atoi(optarg);
@@ -5852,9 +6128,9 @@ static int _process_switches(int *argcp, char ***argvp, const char *dev_dir)
_switches[AREA_SIZE_ARG]++;
_string_args[AREA_SIZE_ARG] = optarg;
}
if (ind == AUX_DATA_ARG) {
_switches[AUX_DATA_ARG]++;
_string_args[AUX_DATA_ARG] = optarg;
if (ind == USER_DATA_ARG) {
_switches[USER_DATA_ARG]++;
_string_args[USER_DATA_ARG] = optarg;
}
if (c == ':' || c == '?')
return_0;
@@ -5882,6 +6158,10 @@ static int _process_switches(int *argcp, char ***argvp, const char *dev_dir)
_switches[MAJOR_ARG]++;
_int_args[MAJOR_ARG] = atoi(optarg);
}
if (ind == REGIONS_ARG) {
_switches[REGIONS_ARG]++;
_string_args[REGIONS_ARG] = optarg;
}
if (c == 'm' || ind == MINOR_ARG) {
_switches[MINOR_ARG]++;
_int_args[MINOR_ARG] = atoi(optarg);
@@ -5904,6 +6184,8 @@ static int _process_switches(int *argcp, char ***argvp, const char *dev_dir)
_switches[PRECISE_ARG]++;
if (ind == RAW_ARG)
_switches[RAW_ARG]++;
if (ind == REGION_ARG)
_switches[REGION_ARG]++;
if (ind == REGION_ID_ARG) {
_switches[REGION_ID_ARG]++;
_int_args[REGION_ID_ARG] = atoi(optarg);
@@ -5966,6 +6248,12 @@ static int _process_switches(int *argcp, char ***argvp, const char *dev_dir)
_switches[GID_ARG]++;
_int_args[GID_ARG] = atoi(optarg);
}
if (ind == GROUP_ARG)
_switches[GROUP_ARG]++;
if (ind == GROUP_ID_ARG) {
_switches[GROUP_ID_ARG]++;
_int_args[GROUP_ID_ARG] = atoi(optarg);
}
if (c == 'U' || ind == UID_ARG) {
_switches[UID_ARG]++;
_int_args[UID_ARG] = atoi(optarg);

View File

@@ -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-2009,2016 Red Hat, Inc. All rights reserved.
*
* This file is part of LVM2.
*
@@ -23,9 +23,46 @@ static int _lv_is_in_vg(struct volume_group *vg, struct logical_volume *lv)
return 1;
}
static struct dm_list *_lvh_in_vg(struct logical_volume *lv, struct volume_group *vg)
{
struct dm_list *lvh;
dm_list_iterate(lvh, &vg->lvs)
if (lv == dm_list_item(lvh, struct lv_list)->lv)
return lvh;
return NULL;
}
static int _lv_tree_move(struct dm_list *lvh,
struct volume_group *vg_from,
struct volume_group *vg_to)
{
uint32_t s;
struct logical_volume *lv = dm_list_item(lvh, struct lv_list)->lv;
struct lv_segment *seg = first_seg(lv);
struct dm_list *lvh1;
dm_list_move(&vg_to->lvs, lvh);
lv->vg = vg_to;
lv->lvid.id[0] = lv->vg->id;
if (seg)
for (s = 0; s < seg->area_count; s++)
if (seg_type(seg, s) == AREA_LV && seg_lv(seg, s)) {
if ((lvh1 = _lvh_in_vg(seg_lv(seg, s), vg_from))) {
if (!_lv_tree_move(lvh1, vg_from, vg_to))
return 0;
} else if (!_lvh_in_vg(seg_lv(seg, s), vg_to))
return 0;
}
return 1;
}
static int _move_one_lv(struct volume_group *vg_from,
struct volume_group *vg_to,
struct dm_list *lvh)
struct volume_group *vg_to,
struct dm_list *lvh)
{
struct logical_volume *lv = dm_list_item(lvh, struct lv_list)->lv;
struct logical_volume *parent_lv;
@@ -38,10 +75,15 @@ static int _move_one_lv(struct volume_group *vg_from,
return 0;
}
dm_list_move(&vg_to->lvs, lvh);
lv->vg = vg_to;
/* Bail out, if any allocations of @lv are still on PVs of @vg_from */
if (lv_is_on_pvs(lv, &vg_from->pvs)) {
log_error("Can't split LV %s between "
"two Volume Groups", lv->name);
return 0;
}
lv->lvid.id[0] = lv->vg->id;
if (!_lv_tree_move(lvh, vg_from, vg_to))
return 0;
/* Moved pool metadata spare LV */
if (vg_from->pool_metadata_spare_lv == lv) {
@@ -148,6 +190,10 @@ static int _move_snapshots(struct volume_group *vg_from,
if (!(lv->status & SNAPSHOT))
continue;
/* Ignore, if no allocations on PVs of @vg_to */
if (!lv_is_on_pvs(lv, &vg_to->pvs))
continue;
dm_list_iterate_items(seg, &lv->segments) {
cow_from = _lv_is_in_vg(vg_from, seg->cow);
origin_from = _lv_is_in_vg(vg_from, seg->origin);
@@ -194,6 +240,10 @@ static int _move_mirrors(struct volume_group *vg_from,
if (!lv_is_mirrored(lv))
continue;
/* Ignore, if no allocations on PVs of @vg_to */
if (!lv_is_on_pvs(lv, &vg_to->pvs))
continue;
seg = first_seg(lv);
seg_in = 0;
@@ -233,13 +283,18 @@ static int _move_mirrors(struct volume_group *vg_from,
return 1;
}
static int _move_raid(struct volume_group *vg_from,
struct volume_group *vg_to)
/*
* Check for any RAID LVs with allocations on PVs of @vg_to.
*
* If these don't have any allocations on PVs of @vg_from,
* move their whole lv stack across to @vg_to including the
* top-level RAID LV.
*/
static int _move_raids(struct volume_group *vg_from,
struct volume_group *vg_to)
{
struct dm_list *lvh, *lvht;
struct logical_volume *lv;
struct lv_segment *seg;
unsigned s, seg_in;
dm_list_iterate_safe(lvh, lvht, &vg_from->lvs) {
lv = dm_list_item(lvh, struct lv_list)->lv;
@@ -247,22 +302,11 @@ static int _move_raid(struct volume_group *vg_from,
if (!lv_is_raid(lv))
continue;
seg = first_seg(lv);
seg_in = 0;
for (s = 0; s < seg->area_count; s++) {
if (_lv_is_in_vg(vg_to, seg_lv(seg, s)))
seg_in++;
if (seg->meta_areas && seg_metalv(seg, s) && _lv_is_in_vg(vg_to, seg_metalv(seg, s)))
seg_in++; /* FIXME Inadequate - must count separately */
}
if (seg_in && seg_in != (seg->area_count * (seg->meta_areas ? 2 : 1))) {
log_error("Can't split RAID %s between "
"two Volume Groups", lv->name);
return 0;
}
/* Ignore, if no allocations on PVs of @vg_to */
if (!lv_is_on_pvs(lv, &vg_to->pvs))
continue;
/* If allocations are on PVs of @vg_to -> move RAID LV stack across */
if (!_move_one_lv(vg_from, vg_to, lvh))
return_0;
}
@@ -283,6 +327,11 @@ static int _move_thins(struct volume_group *vg_from,
if (lv_is_thin_volume(lv)) {
seg = first_seg(lv);
data_lv = seg_lv(first_seg(seg->pool_lv), 0);
/* Ignore, if no allocations on PVs of @vg_to */
if (!lv_is_on_pvs(data_lv, &vg_to->pvs))
continue;
if ((_lv_is_in_vg(vg_to, data_lv) ||
_lv_is_in_vg(vg_to, seg->external_lv))) {
if (_lv_is_in_vg(vg_from, seg->external_lv) ||
@@ -299,6 +348,11 @@ static int _move_thins(struct volume_group *vg_from,
} else if (lv_is_thin_pool(lv)) {
seg = first_seg(lv);
data_lv = seg_lv(seg, 0);
/* Ignore, if no allocations on PVs of @vg_to */
if (!lv_is_on_pvs(data_lv, &vg_to->pvs))
continue;
if (_lv_is_in_vg(vg_to, data_lv) ||
_lv_is_in_vg(vg_to, seg->metadata_lv)) {
if (_lv_is_in_vg(vg_from, seg->metadata_lv) ||
@@ -364,6 +418,12 @@ static int _move_cache(struct volume_group *vg_from,
is_moving = 1;
}
if (!lv_is_on_pvs(data, &vg_to->pvs))
continue;
if (!lv_is_on_pvs(meta, &vg_to->pvs))
continue;
if (orig && (_lv_is_in_vg(vg_to, orig) != is_moving)) {
log_error("Can't split %s and its origin (%s)"
" into separate VGs", lv->name, orig->name);
@@ -601,13 +661,19 @@ int vgsplit(struct cmd_context *cmd, int argc, char **argv)
if (lv_name && !move_pvs_used_by_lv(vg_from, vg_to, lv_name))
goto_bad;
/* Move required LVs across, checking consistency */
if (!(_move_lvs(vg_from, vg_to)))
/*
* First move any required RAID LVs across recursively.
* Reject if they get split between VGs.
*
* This moves the whole LV stack across, thus _move_lvs() below
* ain't hit any of their MetaLVs/DataLVs any more but'll still
* work for all other type specific moves following it.
*/
if (!(_move_raids(vg_from, vg_to)))
goto_bad;
/* FIXME Separate the 'move' from the 'validation' to fix dev stacks */
/* Move required RAID across */
if (!(_move_raid(vg_from, vg_to)))
/* Move required sub LVs across, checking consistency */
if (!(_move_lvs(vg_from, vg_to)))
goto_bad;
/* Move required mirrors across */
@@ -641,8 +707,8 @@ int vgsplit(struct cmd_context *cmd, int argc, char **argv)
/*
* First, write out the new VG as EXPORTED. We do this first in case
* there is a crash - we will still have the new VG information, in an
* exported state. Recovery after this point would be removal of the
* new VG and redoing the vgsplit.
* exported state. Recovery after this point would importing and removal
* of the new VG and redoing the vgsplit.
* FIXME: recover automatically or instruct the user?
*/
vg_to->status |= EXPORTED_VG;