mirror of
git://sourceware.org/git/lvm2.git
synced 2025-09-17 21:44:24 +03:00
Compare commits
41 Commits
v2_03_00
...
dev-bmr-dm
Author | SHA1 | Date | |
---|---|---|---|
|
73111bf33b | ||
|
3b6f7f00f7 | ||
|
42746c7026 | ||
|
a9f1278b55 | ||
|
ced707df28 | ||
|
6fb39fce21 | ||
|
d4d9da6207 | ||
|
6d374475ce | ||
|
cc6177284a | ||
|
c2e043f0c1 | ||
|
59a3705577 | ||
|
76f7f28dac | ||
|
c54da5e82a | ||
|
4b02aa2548 | ||
|
e69a34c15e | ||
|
3914229d60 | ||
|
e25c27e5c2 | ||
|
4ebc6b75cb | ||
|
b7cd377915 | ||
|
8dffd6d217 | ||
|
d94c956903 | ||
|
3626cf025d | ||
|
65606c90d9 | ||
|
973ad7434e | ||
|
14868633cc | ||
|
5160936b15 | ||
|
d6aea67a17 | ||
|
a7ff854ec8 | ||
|
abb499c1c2 | ||
|
728949c7bb | ||
|
9420f82485 | ||
|
f6a999b437 | ||
|
ea2fb03750 | ||
|
f883bca41b | ||
|
7145d666fc | ||
|
f3afd1bd13 | ||
|
b1b32c900a | ||
|
7cec710e37 | ||
|
6c86d94219 | ||
|
05196373be | ||
|
82f4f073bb |
15
WHATS_NEW_DM
15
WHATS_NEW_DM
@@ -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
|
||||
|
14
libdm/.exported_symbols.DM_1_02_129
Normal file
14
libdm/.exported_symbols.DM_1_02_129
Normal 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
|
@@ -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;
|
||||
}
|
||||
|
@@ -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);
|
||||
|
||||
|
2334
libdm/libdm-stats.c
2334
libdm/libdm-stats.c
File diff suppressed because it is too large
Load Diff
210
man/dmstats.8.in
210
man/dmstats.8.in
@@ -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
|
||||
|
454
tools/dmsetup.c
454
tools/dmsetup.c
@@ -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, ®ion_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, ®ion_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);
|
||||
|
132
tools/vgsplit.c
132
tools/vgsplit.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-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;
|
||||
|
Reference in New Issue
Block a user