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

Compare commits

...

15 Commits

Author SHA1 Message Date
Bryn M. Reeves
63e6ef8491 man: add --headers to dmstats.8.in 2015-07-31 15:10:28 +01:00
Bryn M. Reeves
a20989174f man: document --timestamps in dmstats.8.in 2015-07-31 15:10:28 +01:00
Bryn M. Reeves
8df54e25c6 dmsetup: make '--timestamps' set --headers=time
Make '--timestamps' a shorthand for --headers=time.
2015-07-31 15:10:27 +01:00
Bryn M. Reeves
8ca04e316e dmsetup: add an optional header to dmsetup reports
Add optional headers to dmsetup and dmstats reports. These are
selected by the user in a similar way to report fields, e.g.:

Time: 27/07/15 12:07:56           Count:            0
Name             RgID  ArID  RRqM/s   WRqM/s   R/s   W/s   RSz/s WSz/s AvRqS QSize SvcTm Util% AWait
vg_hex-lv_home       0     0     0.00     0.00  0.00 43.00     0 416.00k 9.50k  1.00  1.86  8.00 30.44
vg_hex-lv_root       0     0     0.00     0.00  0.00  0.00     0       0     0  0.00  0.00  0.00  0.00
vg_hex-lv_images     0     0     0.00     0.00  0.00  0.00     0       0     0  0.00  0.00  0.00  0.00

Selects the 'time' and 'report_count' headers to be output.
2015-07-31 15:10:27 +01:00
Bryn M. Reeves
e8eed522a8 dmsetup: add --timestamps switch
Add a switch to optionally print a timestamp before displaying
each report. Use the same format as iostat for now (ISO format
controlled by S_FORMAT_TIME is also easy to add).
2015-07-31 15:10:27 +01:00
Bryn M. Reeves
cd8e3e28e5 libdm-report: add support for optional report headers
Add the ability to output a reow of header data before the main
report. This can be used to print a repeating banner including
data such as the time, the report count, and system performance
metrics.

A 'header' behaves in a similar way to a field; they are defined
by passing in an array of header types and selected using a string
of names. This allows programs using dm_report to customize the
available set of headers and allow their display to be configured
by the user.

Headers do not participate in any way in sorting or selection and
can only appear in the special 'header' section of the report.

A row of headers is added to a report by passing in a string of
header names to be parsed. Header output is either written as
soon as it is defined (unbuffered) or when the library user calls
the dm_report_output_header() function.
2015-07-31 15:10:27 +01:00
Bryn M. Reeves
8cc8b30ba7 man: add dmstats.8 2015-07-31 15:10:27 +01:00
Bryn M. Reeves
1638e090fc dmsetup: add dmstats command
Add arguments, report types, and a 'stats' command that dm-stats will
use and implement 'clear', 'create', 'delete', 'list', 'print', and
'report' sub-commands.

Adapt _display_info_cols() to allow reporting of statistics with the
DR_STATS report type. Since a single object (device) may have many rows
of statistics to report the call to dm_report_object() is placed inside
a loop over each statistics area present.

For non-stats reports or for devices with a single region spanning the
entire device the body of the loop is executed once.

Regions and the areas that they contain are always traversed in
ascending order beginning with area zero of region zero: all sorting is
handled by the report engine.
2015-07-31 15:10:27 +01:00
Bryn M. Reeves
e33656fc53 dmsetup: rename 'char *argc' and 'char ***argv' in _process_switches
Rename these two variables to 'argcp' and 'argvp' to make it clear
we are dealing with pointers to an 'int argc' and 'char **argv'.
2015-07-31 12:57:35 +01:00
Bryn M. Reeves
13d5b8100a libdevmapper: add statistics data structures and interface
Add data structures, type definitions and interfaces to
libdevmapper to work with device-mapper statistics.

To simplify handling gaps in the sequence of region_ids for users
of the library, and to allow for future changes in the data
structures used to contain statistics values in userspace, the data
structures themselves are not exported in libdevmapper.h.

Instead an opaque handle of type struct dm_stats* is obtained by
calling the library and all subsequent statistics operations are
carried out using this handle. A dm_stats object represents the
complete set of available counter sets for an individual mapped
device.

The dm_stats handle contains a pointer to a table of one or more
dm_stats_region objects representing the regions registered with the
@stats_create message. These in turn point to a table of one or more
dm_stats_counters objects containing the counter sets for each defined
area within the region:

  dm_stats->dm_stats_region[nr_regions]->dm_stats_counters[nr_areas]

This structure is private to the library and may change in future
versions: all users should make use of the public interface and treat
the dm_stats type as an opaque handle. Accessor methods are provided
to obtain values stored in individual region and area objects.

Ranges and counter sets are stored in order of increasing device
sector.

Public methods are provided to create and destroy handles and to
list, create, and destroy, statistics regions as well as to obtain and
parse the counter data.

Linux iostat-style derived performance metrics are provided to return
higher-level performance metrics:

    dm_stats_get_throughput()
    dm_stats_get_utilization()
    dm_stats_get_service_time()
    dm_stats_get_rd_merges_per_sec()
    dm_stats_get_wr_merges_per_sec()
    dm_stats_get_reads_per_sec()
    dm_stats_get_read_sectors_per_sec()
    dm_stats_get_writes_per_sec()
    dm_stats_get_write_sectors_per_sec()
    dm_stats_get_average_request_size()
    dm_stats_get_average_queue_size()
    dm_stats_get_await()
    dm_stats_get_r_await()
    dm_stats_get_w_await()
2015-07-31 12:57:35 +01:00
Bryn M. Reeves
077afe5b23 libdm-report: rename dm_report_headings()
Rename dm_report_headings to dm_report_column_headings() to make
it clear that it's the column headings being output.
2015-07-31 12:17:38 +01:00
Bryn M. Reeves
aa4d97d318 libdm-report: fix row and headings leaks
Not releasing objects back to the pool is fine for short-lived
pools since the memory will be freed when dm_pool_destroy() is
called.

Any pool that may be long-lived needs to be more careful to free
objects back to the pool to avoid leaking memory that will not be
reclaimed until the pool is destroyed at process exit time.

The report pool currently leaks each headings lines and some row
data.

Although dm_report_output() tries to free the first allocated row
this may end up freeing a later row due to sorting of the row list
while reporting. Store a pointer to the first allocated row from
_do_report_obect() instead and free this at the end of
_output_as_columns(), _output_as_rows(), and dm_report_clear().

Also make sure to call dm_pool_free() for the headings line built
in _report_headings().

Without these changes dmstats reports can leak around 600k in 10m
(exact rate depends on fields and values):

 top - 12:11:32 up 4 days,  3:16, 15 users,  load average: 0.01, 0.12, 0.14
  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
 6473 root      20   0  130196   3124   2792 S   0.0  0.0   0:00.00 dmstats

 top - 12:22:04 up 4 days,  3:26, 15 users,  load average: 0.06, 0.11, 0.13
  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
 6498 root      20   0  130836   3712   2752 S   0.0  0.0   0:00.60 dmstats

With this patch no increase in RSS is seen:

 top - 13:54:58 up 4 days,  4:59, 15 users,  load average: 0.12, 0.14, 0.14
  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
13962 root      20   0  130196   2996   2688 S   0.0  0.0   0:00.00 dmstats

 top - 14:04:31 up 4 days,  5:09, 15 users,  load average: 1.02, 0.67, 0.36
  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
13962 root      20   0  130196   2996   2688 S   0.3  0.0   0:00.32 dmstats
2015-07-31 12:06:10 +01:00
Bryn M. Reeves
a4b5f8ef39 libdm-report: add dm_report_clear()
Add a call to clear (abandon) a report's current data. This can
be used by callers that make repeating reports, such as dmstats,
in order to throw away the data of the first iteration (which will
have accumulated over some unknown interval).
2015-07-31 12:06:10 +01:00
Bryn M. Reeves
795c590980 libdm-report: add dm_report_headings()
Add a function to output the headings of columns-based reports
even if they have already been shown.

This will be used by dmstats reports to produce iostat-like
repeating reports of statistics values.

This patch removes a check for RH_HEADINGS_PRINTED from
_report_headings that prevents headings being displaye if the flag
is already set; this check is redundant since the only existing
caller (_output_as_columns()) already tests the flag before
calling the function.
2015-07-31 12:06:10 +01:00
Bryn M. Reeves
ba2da29c4c libdm: add interval support to libdm reports
Add functions for dealing with reports that repeat at a set time
interval:

  dm_report_get_interval()
  dm_report_set_interval()
  dm_report_wait_interval()

The fist should be called following dm_report_init() to set the
desired interval to be used with this report. Once the report has
been prepared the program should call dm_report_wait_interval()
to suspend execution until the current interval expires. When the
wait function returns the caller should obtain and output report
data for the new interval.

Measure the actual wait interval in dm_report_wait_interval and
add dm_report_get_last_interval() so that callers can obtain it
to pass to statistics methods.

Make report interval handling consistent everywhere in libdm by
storing the report interval in nanoseconds and adding additional
helper functions to get and set a value in miliseconds. This is
consistent with the other parts of libdm that handle statistics
intervals and removes the need to convert between different
representations within the library - scaling is only needed to
either present a value to the user or to pass to an external
function that expects a particular unit of time (e.g. usleep()).
2015-07-31 12:06:04 +01:00
8 changed files with 5224 additions and 93 deletions

View File

@@ -1,6 +1,90 @@
dm_report_add_header_row
dm_report_clear
dm_report_column_headings
dm_report_get_interval
dm_report_get_interval_ms
dm_report_get_last_interval
dm_report_header
dm_report_header_set_content
dm_report_output_headers
dm_report_set_headers
dm_report_set_interval
dm_report_set_interval_ms
dm_report_wait
dm_size_to_string
dm_stats_bind_devno
dm_stats_bind_name
dm_stats_bind_uuid
dm_stats_buffer_destroy
dm_stats_clear_region
dm_stats_create
dm_stats_create_region
dm_stats_delete_region
dm_stats_destroy
dm_stats_get_area_len
dm_stats_get_area_start
dm_stats_get_average_queue_size
dm_stats_get_average_rd_wait_time
dm_stats_get_average_request_size
dm_stats_get_average_wait_time
dm_stats_get_average_wr_wait_time
dm_stats_get_current_area
dm_stats_get_current_area_len
dm_stats_get_current_area_start
dm_stats_get_current_region
dm_stats_get_current_region_area_len
dm_stats_get_current_region_aux_data
dm_stats_get_current_region_len
dm_stats_get_current_region_program_id
dm_stats_get_current_region_start
dm_stats_get_interval
dm_stats_get_interval_ms
dm_stats_get_io_in_progress
dm_stats_get_io_nsecs
dm_stats_get_rd_merges_per_sec
dm_stats_get_read_nsecs
dm_stats_get_reads
dm_stats_get_read_sectors
dm_stats_get_read_sectors_per_sec
dm_stats_get_reads_merged
dm_stats_get_reads_per_sec
dm_stats_get_region_area_len
dm_stats_get_region_aux_data
dm_stats_get_region_len
dm_stats_get_region_program_id
dm_stats_get_region_start
dm_stats_get_service_time
dm_stats_get_throughput
dm_stats_get_total_read_nsecs
dm_stats_get_total_write_nsecs
dm_stats_get_utilization
dm_stats_get_weighted_io_nsecs
dm_stats_get_write_nsecs
dm_stats_get_writes
dm_stats_get_write_sectors
dm_stats_get_write_sectors_per_sec
dm_stats_get_writes_merged
dm_stats_get_writes_per_sec
dm_stats_get_wr_merges_per_sec
dm_stats_init
dm_stats_list
dm_stats_nr_areas
dm_stats_nr_areas_current
dm_stats_nr_areas_region
dm_stats_nr_regions
dm_stats_populate
dm_stats_populate_region
dm_stats_print_region
dm_stats_region_present
dm_stats_set_interval
dm_stats_set_interval_ms
dm_stats_set_program_id
dm_stats_walk_end
dm_stats_walk_next
dm_stats_walk_next_region
dm_stats_walk_start
dm_timestamp_alloc
dm_timestamp_compare
dm_timestamp_get
dm_timestamp_delta
dm_timestamp_destroy
dm_timestamp_get

View File

@@ -26,6 +26,7 @@ SOURCES =\
libdm-string.c \
libdm-report.c \
libdm-timestamp.c \
libdm-stats.c \
libdm-config.c \
mm/dbg_malloc.c \
mm/pool.c \

View File

@@ -388,6 +388,421 @@ struct dm_status_thin {
int dm_get_status_thin(struct dm_pool *mem, const char *params,
struct dm_status_thin **status);
/**
* device-mapper statistics support
*/
/**
* Statistics handle.
*
* Operations on dm_stats objects include managing statistics regions
* and obtaining and manipulating current counter values from the
* kernel.
*/
struct dm_stats;
/**
* Allocate a dm_stats handle to use for subsequent device-mapper
* statistics operations. A program_id may be specified and will be
* used by default for subsequent operations on this handle.
*
* If program_id is NULL or the empty string a program_id will be
* automatically set to the value contained in /proc/self/comm.
*/
struct dm_stats *dm_stats_create(const char *program_id);
/**
* Bind a dm_stats handle to the specified device major and minor
* values. Any previous binding is cleared and any preexisting counter
* data contained in the handle is released.
*/
int dm_stats_bind_devno(struct dm_stats *dms, int major, int minor);
/**
* Bind a dm_stats handle to the specified device name.
* Any previous binding is cleared and any preexisting counter
* data contained in the handle is released.
*/
int dm_stats_bind_name(struct dm_stats *dms, const char *name);
/**
* Bind a dm_stats handle to the specified device UUID.
* Any previous binding is cleared and any preexisting counter
* data contained in the handle is released.
*/
int dm_stats_bind_uuid(struct dm_stats *dms, const char *uuid);
/**
* Initialise a dm_stats handle so that it can contain at most
* max_regions statistics regions. This must be called prior to
* populating any regions with dm_stats_populate_region().
*
* If dm_stats_init() is called with a stats handle that was previously
* initialised with dm_stats_init(), or another call that initialises
* the region table (e.g. dm_stats_list()), any data contained in the
* handle will be destroyed.
*/
int dm_stats_init(struct dm_stats *dms, uint64_t max_regions);
#define DM_STATS_ALL_PROGRAMS ""
/*
* Parse the response from a @stats_list message. dm_stats_list will
* allocate the necessary dm_stats and dm_stats region structures from
* the embedded dm_pool. No counter data will be obtained (the counters
* members of dm_stats_region objects are set to NULL).
*
* A program_id may optionally be supplied; if the argument is non-NULL
* only regions with a matching program_id value will be considered. If
* the argument is NULL then the default program_id associated with the
* dm_stats handle will be used. Passing the special value
* DM_STATS_ALL_PROGRAMS will cause all regions to be queried
* regardless of region program_id.
*/
int dm_stats_list(struct dm_stats *dms, const char *program_id);
/**
* Populate a region of a dm_stats object with the response from
* a @stats_print message.
*/
int dm_stats_populate_region(struct dm_stats *dms, uint64_t region_id,
const char *resp);
#define DM_STATS_REGIONS_ALL UINT64_MAX
/**
* Populate a dm_stats object with statistics for one or more regions
* of the specified device.
*
* A program_id may optionally be supplied; if the argument is non-NULL
* only regions with a matching program_id value will be considered. If
* the argument is NULL then the default program_id associated with the
* dm_stats handle will be used. Passing the special value
* DM_STATS_ALL_PROGRAMS will cause all regions to be queried
* regardless of region program_id.
*
* Passing the special value DM_STATS_REGIONS_ALL as the region_id
* argument will attempt to retrieve all regions selected by the
* program_id argument.
*
* If region_id is used to request a single region_id to be populated
* the program_id is ignored.
*
*/
int dm_stats_populate(struct dm_stats *dms, const char *program_id,
uint64_t region_id);
/**
* Create a new statistics region on the device bound to dms.
*
* start and len specify the region start and length in 512b sectors.
* Passing zero for both start and len will create a region spanning
* the entire device.
*
* Step determines how to subdivide the region into discrete counter
* sets: a positive value specifies the size of areas into which the
* region should be split while a negative value will split the region
* into a number of areas equal to the absolute value of step:
*
* - a region with one area spanning the entire device:
*
* dm_stats_create_region(dms, 0, 0, -1, p, a);
*
* - a region with areas of 1MiB:
*
* dm_stats_create_region(dms, 0, 0, 1 << 11, p, a);
*
* - one 1MiB region starting at 1024 sectors with two areas:
*
* dm_stats_create_region(dms, 1024, 1 << 11, -2, p, a);
*
* program_id is an optional string argument that identifies the
* program creating the region. If program_id is NULL the default
* program_id stored in the handle will be used.
*
* aux_data is an optional string argument passed to the kernel that is
* stored with the statistics region. It is not accessed by the library
* or kernel and may be used to store arbitrary user data.
*
* The region_id of the newly-created region is returned in *region_id
* if it is non-NULL.
*
*/
int dm_stats_create_region(struct dm_stats *dms, uint64_t *region_id,
uint64_t start, uint64_t len, int64_t step,
const char *program_id, const char *aux_data);
/**
* Delete the specified statistics region. This will also mark the
* region as not-present and discard any existing statistics data.
*/
int dm_stats_delete_region(struct dm_stats *dms, uint64_t region_id);
/**
* Clear the specified statistics region. This requests the kernel to
* zero all counter values (except in-flight I/O). Note that this
* operation is not atomic with respect to reads of the counters; any IO
* events occurring between the last print operation and the clear will
* be lost. This can be avoided by using the atomic print-and-clear
* function of the dm_stats_print_region() call or by using the higher
* level dm_stats_populate*() interface.
*/
int dm_stats_clear_region(struct dm_stats *dms, uint64_t region_id);
/**
* Print the current counter values for the specified statistics region
* and return them as a string. The memory for the string buffer will
* be allocated from the dm_stats handle's private pool and should be
* returned by calling dm_stats_buffer_destroy() when no longer
* required.
*
* This allows applications that wish to access the raw message response
* to obtain it via a dm_stats handle; no parsing of the textual counter
* data is carried out by this function.
*
* Most users are recommended to use the dm_stats_populate* calls
* instead since these automatically parse the statistics data into
* numeric form accessible via the dm_stats_get_*() counter access
* methods.
*
* A subset of the data lines may be requested by setting the
* start_line and num_lines parameters. If both are zero all data
* lines are returned.
*
* If the clear parameter is non-zero the operation will also re-set
* all counter values (except in-flight IO) to zero.
*/
char *dm_stats_print_region(struct dm_stats *dms, uint64_t region_id,
unsigned start_line, unsigned num_lines,
unsigned clear);
/**
* Destroy a statistics message buffer obtained from a call to
* dm_stats_print_region().
*/
void dm_stats_buffer_destroy(struct dm_stats *dms, char *buffer);
/**
* Determine the number of regions contained in a dm_stats handle
* following a dm_stats_list() or dm_stats_populate*() call.
*
* Always returns zero on an empty handle.
*/
int dm_stats_nr_regions(struct dm_stats *dms);
/**
* Test whether region_id is present in this dm_stats handle.
*/
int dm_stats_region_present(struct dm_stats *dms, uint64_t region_id);
/*
* Returns the number of areas (counter sets) contained in the specified
* region_id of the supplied dm_stats handle.
*/
uint64_t dm_stats_nr_areas_region(struct dm_stats *dms, uint64_t region_id);
/*
* Returns the number of areas (counter sets) contained in the current
* region of the supplied dm_stats handle.
*/
uint64_t dm_stats_nr_areas_current(struct dm_stats *dms);
/**
* Returns the total number of areas (counter sets) in all regions of the
* given dm_stats object.
*/
uint64_t dm_stats_nr_areas(struct dm_stats *dms);
/**
* Initialise the cursor of a dm_stats handle to address the first
* present region. It is valid to attempt to walk a NULL stats handle
* or a handle containing no present regions; in this case any call to
* dm_stats_walk_next() becomes a no-op and all calls to
* dm_stats_walk_end() return true.
*/
void dm_stats_walk_start(struct dm_stats *dms);
/**
* Advance the statistics cursor to the next area, or to the next
* present region if at the end of the current region.
*/
void dm_stats_walk_next(struct dm_stats *dms);
/**
* Advance the statistics cursor to the next region.
*/
void dm_stats_walk_next_region(struct dm_stats *dms);
/**
* Test whether the end of a statistics walk has been reached.
*/
int dm_stats_walk_end(struct dm_stats *dms);
/**
* Stats iterators
*
* C 'for' and 'do'/'while' style iterators for dm_stats data.
*
* It is not safe to call any function that modifies the region table
* within the loop body (i.e. dm_stats_list(), dm_stats_populate(),
* dm_stats_init(), or dm_stats_destroy()).
*
* All counter and property (dm_stats_get_*) access methods, as well as
* dm_stats_populate_region() can be safely called from loops.
*
*/
/**
* Iterate over the regions table visiting each region.
*
* If the region table is empty or unpopulated the loop body will not be
* executed.
*/
#define dm_stats_foreach_region(dms) \
for (dm_stats_walk_start((dms)); \
!dm_stats_walk_end((dms)); dm_stats_walk_next_region((dms)))
/**
* Iterate over the regions table visiting each area.
*
* If the region table is empty or unpopulated the loop body will not
* be executed.
*/
#define dm_stats_foreach_area(dms) \
for (dm_stats_walk_start((dms)); \
!dm_stats_walk_end((dms)); dm_stats_walk_next((dms)))
/**
* Start a walk iterating over the regions contained in dm_stats handle
* 'dms'.
*
* The body of the loop should call dm_stats_walk_next() or
* dm_stats_walk_next_region() to advance to the next element.
*
* The loop body is executed at least once even if the stats handle is
* empty.
*/
#define dm_stats_walk_do(dms) \
dm_stats_walk_start((dms)); \
do
/**
* End a loop iterating over the regions contained in dm_stats handle
* 'dms'.
*/
#define dm_stats_walk_while(dms) \
while(!dm_stats_walk_end((dms)))
/**
* Destroy a dm_stats object and all associated regions and counter
* sets.
*/
void dm_stats_destroy(struct dm_stats *dms);
/**
* Counter sampling interval
*/
/**
* Set the sampling interval for counter data to the specified value in
* either nanoseconds or miliseconds.
*
* The interval is used to calculate time-based metrics from the basic
* counter data: an interval must be set before calling any of the
* metric methods.
*
* For best accuracy the duration should be measured and updated at the
* end of each interval.
*
* All values are stored internally with nanosecond precision and are
* converted to or from ms when the milisecond interfaces are used.
*/
void dm_stats_set_interval(struct dm_stats *dms, uint64_t interval);
void dm_stats_set_interval_ms(struct dm_stats *dms, uint64_t interval_ms);
/**
* Retrieve the configured sampling interval in either nanoseconds or
* miliseconds.
*/
uint64_t dm_stats_get_interval(struct dm_stats *dms);
uint64_t dm_stats_get_interval_ms(struct dm_stats *dms);
/**
* Override program_id. This may be used to change the default
* program_id value for an existing handle. If the allow_empty argument
* is non-zero a NULL or empty program_id is permitted.
*
* Use with caution! Most users of the library should set a valid,
* non-NULL program_id for every statistics region created. Failing to
* do so may result in confusing state when multiple programs are
* creating and managing statistics regions.
*
* All users of the library are encouraged to choose an unambiguous,
* unique program_id: this could be based on PID (for programs that
* create, report, and delete regions in a single process), session id,
* executable name, or some other distinguishing string.
*
* Use of the empty string as a program_id does not simplify use of the
* library or the command line tools and use of this value is strongly
* discouraged.
*/
int dm_stats_set_program_id(struct dm_stats *dms, int allow_empty,
const char *program_id);
/**
* Retrieve the current values of the stats cursor.
*/
uint64_t dm_stats_get_current_region(struct dm_stats *dms);
uint64_t dm_stats_get_current_area(struct dm_stats *dms);
/**
* Region properties: size, length & step.
*
* All values are returned in units of 512b sectors.
*/
int dm_stats_get_region_start(struct dm_stats *dms, uint64_t *start,
uint64_t region_id);
int dm_stats_get_region_len(struct dm_stats *dms, uint64_t *len,
uint64_t region_id);
int dm_stats_get_region_area_len(struct dm_stats *dms, uint64_t *area_len,
uint64_t region_id);
int dm_stats_get_current_region_start(struct dm_stats *dms, uint64_t *start);
int dm_stats_get_current_region_len(struct dm_stats *dms, uint64_t *len);
int dm_stats_get_current_region_area_len(struct dm_stats *dms, uint64_t *area_len);
int dm_stats_get_area_start(struct dm_stats *dms, uint64_t *start,
uint64_t region_id, uint64_t area_id);
int dm_stats_get_area_len(struct dm_stats *dms, uint64_t *len,
uint64_t region_id, uint64_t area_id);
int dm_stats_get_current_area_start(struct dm_stats *dms, uint64_t *start);
int dm_stats_get_current_area_len(struct dm_stats *dms, uint64_t *start);
/**
* Retrieve program_id and aux_data for a specific region. Only valid
* following a call to dm_stats_list(). The returned pointer does not
* need to be freed separately from the dm_stats handle but will become
* invalid after a dm_stats_destroy(), dm_stats_list() or dm_stats_bind*()
* of the handle from which it was obtained.
*/
const char *dm_stats_get_region_program_id(struct dm_stats *dms,
uint64_t region_id);
const char *dm_stats_get_region_aux_data(struct dm_stats *dms,
uint64_t region_id);
const char *dm_stats_get_current_region_program_id(struct dm_stats *dms);
const char *dm_stats_get_current_region_aux_data(struct dm_stats *dms);
typedef enum {
DM_STATS_MESSAGE_CREATE, /* region, step, [prog_id, [aux_data]] */
DM_STATS_MESSAGE_DELETE, /* region_id */
DM_STATS_MESSAGE_LIST, /* prog_id */
DM_STATS_MESSAGE_CLEAR, /* region_id */
DM_STATS_MESSAGE_PRINT, /* region_id [start_line, count] */
DM_STATS_MESSAGE_PRINT_CLEAR, /* region_id */
DM_STATS_MESSAGE_SET_AUX /* region_id aux_data */
} dm_stats_message_t;
/*
* Call this to actually run the ioctl.
*/
@@ -1743,6 +2158,7 @@ struct dm_report_field;
#define DM_REPORT_FIELD_TYPE_ID_LEN 32
#define DM_REPORT_FIELD_TYPE_HEADING_LEN 32
#define DM_REPORT_FIELD_TYPE_LABEL_LEN 32
struct dm_report;
struct dm_report_field_type {
@@ -1752,7 +2168,7 @@ struct dm_report_field_type {
int32_t width; /* default width */
/* string used to specify the field */
const char id[DM_REPORT_FIELD_TYPE_ID_LEN];
/* string printed in header */
/* string printed in heading */
const char heading[DM_REPORT_FIELD_TYPE_HEADING_LEN];
int (*report_fn)(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field, const void *data,
@@ -1833,6 +2249,22 @@ typedef int (*dm_report_reserved_handler) (struct dm_report *rh,
const void *data_in,
const void **data_out);
struct dm_report_header;
struct dm_report_header_type {
uint32_t type; /* object type id */
uint32_t flags; /* DM_REPORT_FIELD */
uint32_t offset;
int32_t width;
/* string used to specify the header */
const char id[DM_REPORT_FIELD_TYPE_ID_LEN];
/* string printed in label*/
const char label[DM_REPORT_FIELD_TYPE_LABEL_LEN];
int (*report_fn)(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_header *header, const void *data,
void *private_data);
const char *desc; /* description of the header */
};
/*
* The dm_report_value_cache_{set,get} are helper functions to store and retrieve
* various values used during reporting (dm_report_field_type.report_fn) and/or
@@ -1851,6 +2283,8 @@ const void *dm_report_value_cache_get(struct dm_report *rh, const char *name);
#define DM_REPORT_OUTPUT_FIELD_NAME_PREFIX 0x00000008
#define DM_REPORT_OUTPUT_FIELD_UNQUOTED 0x00000010
#define DM_REPORT_OUTPUT_COLUMNS_AS_ROWS 0x00000020
#define DM_REPORT_OUTPUT_HEADERS 0x00000040
#define DM_REPORT_OUTPUT_HEADER_LABELS 0x00000040
struct dm_report *dm_report_init(uint32_t *report_types,
const struct dm_report_object_type *types,
@@ -1882,6 +2316,23 @@ int dm_report_object(struct dm_report *rh, void *object);
*/
int dm_report_object_is_selected(struct dm_report *rh, void *object, int do_output, int *selected);
/*
* Update header fields printed before each report.
*/
int dm_report_header(struct dm_report *rh, void *object);
/*
* Add a row of headers to be output before each report. Currently only
* a single row of header data is supported.
*/
int dm_report_add_header_row(struct dm_report *rh, const char *output_headers);
/*
* Set the list of available header fields for this report.
*/
int dm_report_set_headers(struct dm_report *rh,
const struct dm_report_header_type *headers);
/*
* Compact report output so that if field value is empty for all rows in
* the report, drop the field from output completely (including headers).
@@ -1891,6 +2342,21 @@ int dm_report_object_is_selected(struct dm_report *rh, void *object, int do_outp
int dm_report_compact_fields(struct dm_report *rh);
int dm_report_output(struct dm_report *rh);
int dm_report_output_headers(struct dm_report *rh);
/**
* Clear the current report's data without reporting it.
*/
int dm_report_clear(struct dm_report *rh);
/**
* Output the report headings for a columns-based report, even if they
* have already been shown. Useful for repeating reports that wish to
* issue a periodic reminder of the column headings.
*/
int dm_report_column_headings(struct dm_report *rh);
void dm_report_free(struct dm_report *rh);
/*
@@ -1928,6 +2394,161 @@ int dm_report_field_percent(struct dm_report *rh, struct dm_report_field *field,
void dm_report_field_set_value(struct dm_report_field *field, const void *value,
const void *sortvalue);
/*
* For custom header content, allocate the data in 'mem' and use
* dm_report_header_set_content().
*/
void dm_report_header_set_content(struct dm_report_header *hdr,
const void *content);
/*
* Set an interval (in miliseconds) for this dm_report object that will
* be used by any subsequent call to dm_report_wait_interval. This is
* only useful for repeating reports (e.g. statistics).
*
* The default value is zero: no interval.
*/
void dm_report_set_interval(struct dm_report *rh, uint64_t interval);
/*
* Set an interval in miliseconds for this dm_report object that will
* be used by any subsequent call to dm_report_wait. This is only
* useful for repeating reports (e.g. statistics).
*
* The default value is zero: no interval.
*/
void dm_report_set_interval_ms(struct dm_report *rh, uint64_t interval_ms);
/**
* Retrieve the configured interval of the dm_report handle rh in
* nanoseconds.
*/
uint64_t dm_report_get_interval(struct dm_report *rh);
/**
* Retrieve the configured interval of the dm_report handle rh in
* miliseconds.
*/
uint64_t dm_report_get_interval_ms(struct dm_report *rh);
/**
* Retrieve the duration of the last interval of this dm_report handle
* in nanoseconds.
*/
uint64_t dm_report_get_last_interval(struct dm_report *rh);
/*
* Suspend the calling thread until the current reporting interval
* expires. When this function returns the caller should obtain updated
* report data and call dm_report_object() and dm_report_output() as
* necessary in order to produce the new interval's reporting output.
*
* Delivery of a non-blocked signal to the thread carrying out the
* wait will cause the function to return prematurely with an error.
*
* Attempting to wait on a report that has no interval set is also
* treated as an error.
*
* If waited is non-NULL the actual duration of the wait in ns will
* be returned.
*/
int dm_report_wait(struct dm_report *rh);
/**
* Stats counter access methods
*
* Each method returns the corresponding stats counter value from the
* supplied dm_stats handle for the specified region_id and area_id.
* If either region_id or area_id uses one of the special values
* DM_STATS_REGION_CURRENT or DM_STATS_AREA_CURRENT then the region
* or area is selected according to the current state of the dm_stats
* handle's embedded cursor.
*/
#define DM_STATS_REGION_CURRENT UINT64_MAX
#define DM_STATS_AREA_CURRENT UINT64_MAX
uint64_t dm_stats_get_reads(struct dm_stats *dms,
uint64_t region_id, uint64_t area_id);
uint64_t dm_stats_get_reads_merged(struct dm_stats *dms,
uint64_t region_id, uint64_t area_id);
uint64_t dm_stats_get_read_sectors(struct dm_stats *dms,
uint64_t region_id, uint64_t area_id);
uint64_t dm_stats_get_read_nsecs(struct dm_stats *dms,
uint64_t region_id, uint64_t area_id);
uint64_t dm_stats_get_writes(struct dm_stats *dms,
uint64_t region_id, uint64_t area_id);
uint64_t dm_stats_get_writes_merged(struct dm_stats *dms,
uint64_t region_id, uint64_t area_id);
uint64_t dm_stats_get_write_sectors(struct dm_stats *dms,
uint64_t region_id, uint64_t area_id);
uint64_t dm_stats_get_write_nsecs(struct dm_stats *dms,
uint64_t region_id, uint64_t area_id);
uint64_t dm_stats_get_io_in_progress(struct dm_stats *dms,
uint64_t region_id, uint64_t area_id);
uint64_t dm_stats_get_io_nsecs(struct dm_stats *dms,
uint64_t region_id, uint64_t area_id);
uint64_t dm_stats_get_weighted_io_nsecs(struct dm_stats *dms,
uint64_t region_id, uint64_t area_id);
uint64_t dm_stats_get_total_read_nsecs(struct dm_stats *dms,
uint64_t region_id, uint64_t area_id);
uint64_t dm_stats_get_total_write_nsecs(struct dm_stats *dms,
uint64_t region_id, uint64_t area_id);
int dm_stats_get_rd_merges_per_sec(struct dm_stats *dms, double *rrqm,
uint64_t region_id, uint64_t area_id);
int dm_stats_get_wr_merges_per_sec(struct dm_stats *dms, double *rrqm,
uint64_t region_id, uint64_t area_id);
int dm_stats_get_reads_per_sec(struct dm_stats *dms, double *rd_s,
uint64_t region_id, uint64_t area_id);
int dm_stats_get_writes_per_sec(struct dm_stats *dms, double *wr_s,
uint64_t region_id, uint64_t area_id);
int dm_stats_get_read_sectors_per_sec(struct dm_stats *dms, double *rsec_s,
uint64_t region_id, uint64_t area_id);
int dm_stats_get_write_sectors_per_sec(struct dm_stats *dms, double *wr_s,
uint64_t region_id, uint64_t area_id);
int dm_stats_get_average_request_size(struct dm_stats *dms, double *arqsz,
uint64_t region_id, uint64_t area_id);
int dm_stats_get_service_time(struct dm_stats *dms, double *svctm,
uint64_t region_id, uint64_t area_id);
int dm_stats_get_average_queue_size(struct dm_stats *dms, double *qusz,
uint64_t region_id, uint64_t area_id);
int dm_stats_get_average_wait_time(struct dm_stats *dms, double *await,
uint64_t region_id, uint64_t area_id);
int dm_stats_get_average_rd_wait_time(struct dm_stats *dms, double *await,
uint64_t region_id, uint64_t area_id);
int dm_stats_get_average_wr_wait_time(struct dm_stats *dms, double *await,
uint64_t region_id, uint64_t area_id);
int dm_stats_get_throughput(struct dm_stats *dms, double *tput,
uint64_t region_id, uint64_t area_id);
int dm_stats_get_utilization(struct dm_stats *dms, dm_percent_t *util,
uint64_t region_id, uint64_t area_id);
/*************************
* config file parse/print
*************************/

View File

@@ -35,6 +35,12 @@ struct selection {
struct dm_report {
struct dm_pool *mem;
/**
* Cache the first row allocated so that all rows and fields
* can be disposed of in a single dm_pool_free() call.
*/
struct row *first_row;
/* To report all available types */
#define REPORT_TYPES_ALL UINT32_MAX
uint32_t report_types;
@@ -42,15 +48,28 @@ struct dm_report {
const char *field_prefix;
uint32_t flags;
const char *separator;
/* reporting interval in ms */
uint32_t interval;
/* duration of last wait in ns */
uint64_t last_wait;
uint32_t keys_count;
/* Ordered list of headers to be output before the report. */
struct dm_list header_props;
/* Rows of headers */
struct dm_list header_rows;
/* Ordered list of fields needed for this report */
struct dm_list field_props;
/* Rows of report data */
struct dm_list rows;
/* Array of header definitions */
const struct dm_report_header_type *headers;
/* Array of field definitions */
const struct dm_report_field_type *fields;
const struct dm_report_object_type *types;
@@ -85,6 +104,14 @@ struct field_properties {
int implicit;
};
struct header_properties {
struct dm_list list;
uint32_t hdr_num;
int32_t width;
const struct dm_report_object_type *type;
uint32_t flags;
};
/*
* Report selection
*/
@@ -222,6 +249,18 @@ struct row {
int selected;
};
struct dm_report_header {
struct dm_list list;
struct header_properties *props;
const char *header_string;
};
struct header_row {
struct dm_list list;
struct dm_report *rh;
struct dm_list headers;
};
/*
* Implicit report types and fields.
*/
@@ -664,6 +703,11 @@ void dm_report_field_set_value(struct dm_report_field *field, const void *value,
log_warn(INTERNAL_ERROR "Using string as sort value for numerical field.");
}
void dm_report_header_set_content(struct dm_report_header *hdr, const void *content)
{
hdr->header_string = (const char *) content;
}
static const char *_get_field_type_name(unsigned field_type)
{
switch (field_type) {
@@ -692,6 +736,18 @@ static size_t _get_longest_field_id_len(const struct dm_report_field_type *field
return id_len;
}
static size_t _get_longest_header_id_len(const struct dm_report_header_type *headers)
{
uint32_t f;
size_t id_len = 0;
for (f = 0; headers[f].report_fn; f++)
if (strlen(headers[f].id) > id_len)
id_len = strlen(headers[f].id);
return id_len;
}
static void _display_fields_more(struct dm_report *rh,
const struct dm_report_field_type *fields,
size_t id_len, int display_all_fields_item,
@@ -736,6 +792,45 @@ static void _display_fields_more(struct dm_report *rh,
}
}
static void _display_headers_more(struct dm_report *rh,
const struct dm_report_header_type *headers,
size_t id_len, int display_all_headers_item)
{
uint32_t h;
const struct dm_report_object_type *type;
const char *desc, *last_desc = "";
for (h = 0; headers[h].report_fn; h++)
if (strlen(headers[h].id) > id_len)
id_len = strlen(headers[h].id);
for (type = rh->types; type->data_fn; type++)
if (strlen(type->prefix) + 3 > id_len)
id_len = strlen(type->prefix) + 3;
for (h = 0; headers[h].report_fn; h++) {
if ((type = _find_type(rh, headers[h].type)) && type->desc)
desc = type->desc;
else
desc = " ";
if (desc != last_desc) {
if (*last_desc)
log_warn(" ");
log_warn("%s Fields", desc);
log_warn("%*.*s", (int) strlen(desc) + 7,
(int) strlen(desc) + 7,
"-------------------------------------------------------------------------------");
if (display_all_headers_item && type->id != SPECIAL_REPORT_TYPE)
log_warn(" %sall%-*s - %s", type->prefix,
(int) (id_len - 3 - strlen(type->prefix)), "",
"All headers in this section.");
}
/* FIXME Add line-wrapping at terminal width (or 80 cols) */
log_warn(" %-*s - %s", (int) id_len, headers[h].id, headers[h].desc);
last_desc = desc;
}
}
/*
* show help message
*/
@@ -757,6 +852,20 @@ static void _display_fields(struct dm_report *rh, int display_all_fields_item,
}
/*
* show help message
*/
static void _display_headers(struct dm_report *rh, int display_all_headers_item,
int display_header_types)
{
size_t tmp, id_len = 0;
if ((tmp = _get_longest_header_id_len(rh->headers)) > id_len)
id_len = tmp;
_display_headers_more(rh, rh->headers, id_len, display_all_headers_item);
}
/*
* Initialise report handle
*/
@@ -788,7 +897,7 @@ static struct field_properties * _add_field(struct dm_report *rh,
{
struct field_properties *fp;
if (!(fp = dm_pool_zalloc(rh->mem, sizeof(struct field_properties)))) {
if (!(fp = dm_pool_zalloc(rh->mem, sizeof(*fp)))) {
log_error("dm_report: struct field_properties allocation "
"failed");
return NULL;
@@ -814,12 +923,55 @@ static struct field_properties * _add_field(struct dm_report *rh,
return fp;
}
static int _copy_header(struct dm_report *rh, struct header_properties *dest,
uint32_t hdr_num)
{
const struct dm_report_header_type *headers = rh->headers;
dest->hdr_num = hdr_num;
dest->width = headers[hdr_num].width;
dest->flags = headers[hdr_num].flags & DM_REPORT_FIELD_MASK;
/* set object type method */
dest->type = _find_type(rh, headers[hdr_num].type);
if (!dest->type) {
log_error("dm_report: no matching header: %s",
headers[hdr_num].id);
return 0;
}
return 1;
}
static struct header_properties * _add_header(struct dm_report *rh,
uint32_t hdr_num, uint32_t flags)
{
struct header_properties *hp;
if (!(hp = dm_pool_zalloc(rh->mem, sizeof(*hp)))) {
log_error("dm_report: struct header_properties allocation "
"failed");
return NULL;
}
if (!_copy_header(rh, hp, hdr_num)) {
stack;
dm_pool_free(rh->mem, hp);
return NULL;
}
hp->flags |= flags;
dm_list_add(&rh->header_props, &hp->list);
return hp;
}
/*
* Compare name1 against name2 or prefix plus name2
* name2 is not necessarily null-terminated.
* len2 is the length of name2.
*/
static int _is_same_field(const char *name1, const char *name2,
static int _is_same_name(const char *name1, const char *name2,
size_t len2, const char *prefix)
{
size_t prefix_len;
@@ -905,7 +1057,7 @@ static int _get_field(struct dm_report *rh, const char *field, size_t flen,
return 0;
for (f = 0; _implicit_report_fields[f].report_fn; f++) {
if (_is_same_field(_implicit_report_fields[f].id, field, flen, rh->field_prefix)) {
if (_is_same_name(_implicit_report_fields[f].id, field, flen, rh->field_prefix)) {
*f_ret = f;
*implicit = 1;
return 1;
@@ -913,7 +1065,7 @@ static int _get_field(struct dm_report *rh, const char *field, size_t flen,
}
for (f = 0; rh->fields[f].report_fn; f++) {
if (_is_same_field(rh->fields[f].id, field, flen, rh->field_prefix)) {
if (_is_same_name(rh->fields[f].id, field, flen, rh->field_prefix)) {
*f_ret = f;
*implicit = 0;
return 1;
@@ -952,6 +1104,51 @@ static int _field_match(struct dm_report *rh, const char *field, size_t flen,
return 0;
}
/*
* Add all headers with a matching type.
*/
static int _add_all_headers(struct dm_report *rh, uint32_t type)
{
uint32_t h;
for (h = 0; rh->headers[h].report_fn; h++)
if ((rh->headers[h].type & type) && !_add_header(rh, h, 0))
return 0;
return 1;
}
static int _get_header(struct dm_report *rh, const char *header,
size_t hlen, uint32_t *h_ret)
{
uint32_t h;
if (!hlen)
return 0;
for (h = 0; rh->headers[h].report_fn; h++) {
if (_is_same_name(rh->headers[h].id, header, hlen, "")) {
*h_ret = h;
return 1;
}
}
return 0;
}
static int _header_match(struct dm_report *rh, const char *header, size_t hlen)
{
uint32_t h /*, type*/;
if (!hlen)
return 0;
if ((_get_header(rh, header, hlen, &h)))
return _add_header(rh, h, 0) ? 1 : 0;
return 0;
}
static int _add_sort_key(struct dm_report *rh, uint32_t field_num, int implicit,
uint32_t flags, unsigned report_type_only)
{
@@ -1015,11 +1212,11 @@ static int _key_match(struct dm_report *rh, const char *key, size_t len,
}
for (f = 0; _implicit_report_fields[f].report_fn; f++)
if (_is_same_field(_implicit_report_fields[f].id, key, len, rh->field_prefix))
if (_is_same_name(_implicit_report_fields[f].id, key, len, rh->field_prefix))
return _add_sort_key(rh, f, 1, flags, report_type_only);
for (f = 0; rh->fields[f].report_fn; f++)
if (_is_same_field(rh->fields[f].id, key, len, rh->field_prefix))
if (_is_same_name(rh->fields[f].id, key, len, rh->field_prefix))
return _add_sort_key(rh, f, 0, flags, report_type_only);
return 0;
@@ -1079,6 +1276,32 @@ static int _parse_keys(struct dm_report *rh, const char *keys,
return 1;
}
static int _parse_headers(struct dm_report *rh, const char *format)
{
const char *ws; /* Word start */
const char *we = format; /* Word end */
while (*we) {
/* Allow consecutive commas */
while (*we && *we == ',')
we++;
/* start of the header name */
ws = we;
while (*we && *we != ',')
we++;
if (!_header_match(rh, ws, (size_t) (we - ws))) {
_display_headers(rh, 1, 0);
log_warn(" ");
log_error("Unrecognised header: %.*s", (int) (we - ws), ws);
return 0;
}
}
return 1;
}
static int _contains_reserved_report_type(const struct dm_report_object_type *types)
{
const struct dm_report_object_type *type, *implicit_type;
@@ -1159,6 +1382,7 @@ struct dm_report *dm_report_init(uint32_t *report_types,
rh->fields = fields;
rh->types = types;
rh->private = private_data;
rh->interval = 0; /* no interval */
rh->flags |= output_flags & DM_REPORT_OUTPUT_MASK;
@@ -1174,6 +1398,8 @@ struct dm_report *dm_report_init(uint32_t *report_types,
rh->flags |= RH_SORT_REQUIRED;
dm_list_init(&rh->field_props);
dm_list_init(&rh->header_props);
dm_list_init(&rh->header_rows);
dm_list_init(&rh->rows);
if ((type = _find_type(rh, rh->report_types)) && type->prefix)
@@ -1240,6 +1466,21 @@ static char *_toupperstr(char *str)
return str;
}
int dm_report_set_headers(struct dm_report *rh,
const struct dm_report_header_type *headers)
{
if (headers)
rh->headers = headers;
else
return 0;
return 1;
}
int dm_report_add_header_row(struct dm_report *rh, const char *output_headers)
{
return _parse_headers(rh, output_headers);
}
int dm_report_set_output_field_name_prefix(struct dm_report *rh, const char *output_field_name_prefix)
{
char *prefix;
@@ -1280,6 +1521,21 @@ static void *_report_get_implicit_field_data(struct dm_report *rh __attribute__(
return NULL;
}
/*
* Create part of a line of header data
*/
static void *_report_get_header_data(struct dm_report *rh, struct
header_properties *hp, void *object)
{
const struct dm_report_header_type *headers = rh->headers;
char *ret = hp->type->data_fn(object);
if (!ret)
return NULL;
return (void *)(ret + headers[hp->hdr_num].offset);
}
static int _dbl_equal(double d1, double d2)
{
return fabs(d1 - d2) < DBL_EPSILON;
@@ -1801,6 +2057,9 @@ static int _do_report_object(struct dm_report *rh, void *object, int do_output,
return 0;
}
if (!rh->first_row)
rh->first_row = row;
row->rh = rh;
if ((rh->flags & RH_SORT_REQUIRED) &&
@@ -1967,6 +2226,82 @@ int dm_report_object_is_selected(struct dm_report *rh, void *object, int do_outp
return _do_report_object(rh, object, do_output, selected);
}
int dm_report_header(struct dm_report *rh, void *object)
{
const struct dm_report_header_type *headers;
struct header_properties *hp;
struct header_row *row = NULL;
struct dm_report_header *header;
void *data = NULL;
int len;
int r = 0;
if (!rh) {
log_error(INTERNAL_ERROR "dm_report_header: dm_report handler is NULL.");
return 0;
}
if (rh->flags & RH_ALREADY_REPORTED)
return 1;
if (!(row = dm_pool_zalloc(rh->mem, sizeof(*row)))) {
log_error("dm_report_header: struct header_row allocation failed");
return 0;
}
row->rh = rh;
headers = rh->headers;
dm_list_init(&row->headers);
/* For each field to be displayed, call its report_fn */
dm_list_iterate_items(hp, &rh->header_props) {
if (!(header = dm_pool_zalloc(rh->mem, sizeof(*header)))) {
log_error("do_report_header: "
"struct dm_report_header allocation failed");
goto out;
}
header->props = hp;
data = _report_get_header_data(rh, hp, object);
if (!data) {
log_error("do_report_header:"
"no data assigned to header %s",
headers[hp->hdr_num].id);
goto out;
}
if (!headers[hp->hdr_num].report_fn(rh, rh->mem,
header, data,
rh->private)) {
log_error("do_report_header:"
"report function failed for header %s",
headers[hp->hdr_num].id);
goto out;
}
dm_list_add(&row->headers, &header->list);
}
r = 1;
dm_list_add(&rh->header_rows, &row->list);
dm_list_iterate_items(header, &row->headers) {
len = (int) strlen(header->header_string);
if ((len > header->props->width))
header->props->width = len;
}
if (!(rh->flags & DM_REPORT_OUTPUT_BUFFERED))
return dm_report_output_headers(rh);
out:
if (!r)
dm_pool_free(rh->mem, row);
return r;
}
/*
* Selection parsing
*/
@@ -3835,9 +4170,6 @@ static int _report_headings(struct dm_report *rh)
char *buf = NULL;
size_t buf_size = 0;
if (rh->flags & RH_HEADINGS_PRINTED)
return 1;
rh->flags |= RH_HEADINGS_PRINTED;
if (!(rh->flags & DM_REPORT_OUTPUT_HEADINGS))
@@ -3894,8 +4226,12 @@ static int _report_headings(struct dm_report *rh)
log_error("dm_report: Failed to generate report headings for printing");
goto bad;
}
log_print("%s", (char *) dm_pool_end_object(rh->mem));
/* print all headings */
heading = (char *) dm_pool_end_object(rh->mem);
log_print("%s", heading);
dm_pool_free(rh->mem, (void *)heading);
dm_free(buf);
return 1;
@@ -3906,6 +4242,11 @@ static int _report_headings(struct dm_report *rh)
return 0;
}
int dm_report_column_headings(struct dm_report *rh)
{
return _report_headings(rh);
}
/*
* Sort rows of data
*/
@@ -4084,6 +4425,86 @@ bad:
return 0;
}
/*
* Produce header output
*/
static int _output_header(struct dm_report *rh, struct dm_report_header *header)
{
int32_t width, labelwidth;
uint32_t align;
const char *hdrstr, *labelstr;
char *buf = NULL;
size_t buf_size = 0;
if (rh->flags & DM_REPORT_OUTPUT_HEADER_LABELS)
labelstr = rh->headers[header->props->hdr_num].label;
else
labelstr = "";
labelwidth = strlen(labelstr) + 1;
hdrstr = header->header_string;
width = header->props->width - labelwidth;
if (!(rh->flags & DM_REPORT_OUTPUT_ALIGNED)) {
if (!dm_pool_grow_object(rh->mem, hdrstr, 0)) {
log_error("dm_report: Unable to extend output line");
return 0;
}
} else {
if (!(align = header->props->flags & DM_REPORT_FIELD_ALIGN_MASK))
align = ((header->props->flags & DM_REPORT_FIELD_TYPE_NUMBER) ||
(header->props->flags & DM_REPORT_FIELD_TYPE_SIZE)) ?
DM_REPORT_FIELD_ALIGN_RIGHT : DM_REPORT_FIELD_ALIGN_LEFT;
/* Including trailing '\0'! */
buf_size = labelwidth + width + 1;
if (!(buf = dm_malloc(buf_size))) {
log_error("dm_report: Could not allocate memory for output line buffer.");
return 0;
}
if (align & DM_REPORT_FIELD_ALIGN_LEFT) {
if (dm_snprintf(buf, buf_size, "%-*.*s%-*.*s",
/* FIXME: handle label width better */
labelwidth, labelwidth, labelstr, width, width, hdrstr) < 0) {
log_error("dm_report: left-aligned snprintf() failed");
goto bad;
}
if (!dm_pool_grow_object(rh->mem, buf, labelwidth + width)) {
log_error("dm_report: Unable to extend output line");
goto bad;
}
} else if (align & DM_REPORT_FIELD_ALIGN_RIGHT) {
if (dm_snprintf(buf, buf_size, "%*.*s%*.*s",
/* FIXME: handle label width better */
labelwidth, labelwidth, labelstr, width, width, hdrstr) < 0) {
log_error("dm_report: right-aligned snprintf() failed");
goto bad;
}
if (!dm_pool_grow_object(rh->mem, buf, labelwidth + width)) {
log_error("dm_report: Unable to extend output line");
goto bad;
}
}
}
dm_free(buf);
return 1;
bad:
dm_free(buf);
return 0;
}
static void _destroy_rows(struct dm_report *rh)
{
/* free the first row allocated to this report */
if(rh->first_row)
dm_pool_free(rh->mem, rh->first_row);
rh->first_row = NULL;
dm_list_init(&rh->rows);
}
static int _output_as_rows(struct dm_report *rh)
{
const struct dm_report_field_type *fields;
@@ -4139,6 +4560,8 @@ static int _output_as_rows(struct dm_report *rh)
log_print("%s", (char *) dm_pool_end_object(rh->mem));
}
_destroy_rows(rh);
return 1;
bad:
@@ -4187,8 +4610,7 @@ static int _output_as_columns(struct dm_report *rh)
dm_list_del(&row->list);
}
if (row)
dm_pool_free(rh->mem, row);
_destroy_rows(rh);
return 1;
@@ -4197,6 +4619,27 @@ static int _output_as_columns(struct dm_report *rh)
return 0;
}
int dm_report_clear(struct dm_report *rh)
{
struct dm_list *fh, *rowh, *ftmp, *rtmp;
struct row *row = NULL;
struct dm_report_field *field;
/* clear buffer */
dm_list_iterate_safe(rowh, rtmp, &rh->rows) {
row = dm_list_item(rowh, struct row);
dm_list_iterate_safe(fh, ftmp, &row->fields) {
field = dm_list_item(fh, struct dm_report_field);
dm_list_del(&field->list);
}
dm_list_del(&row->list);
}
_destroy_rows(rh);
return 1;
}
int dm_report_output(struct dm_report *rh)
{
if (dm_list_empty(&rh->rows))
@@ -4210,3 +4653,128 @@ int dm_report_output(struct dm_report *rh)
else
return _output_as_columns(rh);
}
int dm_report_output_headers(struct dm_report *rh)
{
struct dm_list *hh, *rowh, *htmp;
struct header_row *row = NULL;
struct dm_report_header *header;
/* Print and clear buffer */
dm_list_iterate_safe(rowh, htmp, &rh->header_rows) {
if (!dm_pool_begin_object(rh->mem, 512)) {
log_error("dm_report: Unable to allocate output line");
return 0;
}
row = dm_list_item(rowh, struct header_row);
/* don't attempt to print an empty header row. */
if (dm_list_empty(&row->headers)) {
dm_pool_abandon_object(rh->mem);
dm_list_del(&row->list);
continue;
}
dm_list_iterate_safe(hh, htmp, &row->headers) {
header = dm_list_item(hh, struct dm_report_header);
if (!_output_header(rh, header))
goto bad;
dm_list_del(&header->list);
if (!dm_list_end(&row->headers, hh))
if (!dm_pool_grow_object(rh->mem, rh->separator, 0)) {
log_error("dm_report: Unable to extend header output line");
goto bad;
}
}
if (!dm_pool_grow_object(rh->mem, "\0", 1)) {
log_error("dm_report: Unable to terminate header output line");
goto bad;
}
log_print("%s", (char *) dm_pool_end_object(rh->mem));
dm_list_del(&row->list);
if (dm_list_end(&rh->header_rows, rowh))
break;
}
dm_pool_free(rh->mem, row);
return 1;
bad:
dm_pool_abandon_object(rh->mem);
return 0;
}
#define NSEC_PER_USEC 1000L
#define NSEC_PER_MSEC 1000000L
#define NSEC_PER_SEC 1000000000L
void dm_report_set_interval(struct dm_report *rh, uint64_t interval)
{
rh->interval = interval;
}
void dm_report_set_interval_ms(struct dm_report *rh, uint64_t interval_ms)
{
rh->interval = interval_ms * NSEC_PER_MSEC;
}
uint64_t dm_report_get_interval(struct dm_report *rh)
{
return rh->interval;
}
uint64_t dm_report_get_interval_ms(struct dm_report *rh)
{
return (rh->interval / NSEC_PER_MSEC);
}
uint64_t dm_report_get_last_interval(struct dm_report *rh)
{
if (rh->last_wait)
return rh->last_wait;
return rh->interval;
}
int dm_report_wait(struct dm_report *rh)
{
struct dm_timestamp *ts_start, *ts_now;
int r = 1;
if (!rh->interval)
return_0;
if(!(ts_start = dm_timestamp_alloc()))
return_0;
if(!(ts_now = dm_timestamp_alloc()))
return_0;
if (!dm_timestamp_get(ts_start)) {
log_error("Could not obtain initial timestamp.");
return 0;
}
if (usleep(rh->interval / NSEC_PER_USEC)) {
if (errno == EINTR)
log_error("Report interval interrupted by signal.");
if (errno == EINVAL)
log_error("Report interval too short.");
r = 0;
}
if (!dm_timestamp_get(ts_now)) {
log_error("Could not obtain current timestamp.");
return 0;
}
/* store interval duration in nanoseconds */
rh->last_wait = dm_timestamp_delta(ts_now, ts_start);
dm_timestamp_destroy(ts_start);
dm_timestamp_destroy(ts_now);
return r;
}

1365
libdm/libdm-stats.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -83,7 +83,7 @@ ifneq ("@THIN@", "none")
MAN7+=lvmthin.7
endif
MAN8DM=dmsetup.8 $(DMEVENTDMAN) $(BLKDEACTIVATEMAN)
MAN8DM=dmsetup.8 dmstats.8 $(DMEVENTDMAN) $(BLKDEACTIVATEMAN)
MAN5DIR=$(mandir)/man5
MAN7DIR=$(mandir)/man7
MAN8DIR=$(mandir)/man8

712
man/dmstats.8.in Normal file
View File

@@ -0,0 +1,712 @@
.TH DMSTATS 8 "Jul 25 2015" "Linux" "MAINTENANCE COMMANDS"
.SH NAME
dmstats \(em device-mapper statistics management
.SH SYNOPSIS
.ad l
.B dmsetup stats
.I command
.RB [ options ]
.br
.B dmstats <command>
.RB [[
.IR device_name ]
.RB |[ \-\-uuid
.IR uuid ]
.RB |[ \-\-major
.IR major
.RB \-\-minor
.IR minor ]]
.br
.B dmstats clear
.I device_name
.RB [ \-\-allregions
.RB | \-\-regionid
.IR id ]
.br
.B dmstats create
.I device_name
.RB [[ \-\-areas
.IR nr_areas ]
.RB |[ \-\-areasize
.IR area_size ]]
.RB [[ \-\-start
.IR start_sector ]
.RB [ \-\-length
.IR length ]
.RB |[ \-\-targets ]]
.RB [ \-\-auxdata
.IR data ]
.RB [ \-\-programid
.IR id ]
.br
.B dmstats delete
.I device_name
.RB [ \-\-force ]
.RB [ \-\-allregions
.RB | \-\-regionid
.IR id ]
.RB [ \-\-allprograms
.RB | \-\-programid
.IR id ]
.br
.B dmstats help
.RB [ \-c | \-C | \-\-columns ]
.br
.B dmstats list
.RI [ device_name ]
.RB [ \-\-allprograms
.RB | \-\-programid
.IR id ]
.RB [ \-\-units
.IR units ]
.RB [ \-\-nosuffix ]
.br
.B dmstats print
.RI [ device_name ]
.RB [ \-\-clear ]
.RB [ \-\-allprograms
.RB | \-\-programid
.IR id ]
.RB [ \-\-allregions
.RB | \-\-regionid
.IR id ]
.br
.B dmstats report
.RI [ device_name ]
.RB [ \-\-interval
.IR seconds ]
.RB [ \-\-count
.IR count ]
.RB [ \-\-timestamps ]
.RB [ \-\-units
.IR units ]
.RB [ \-\-allprograms ]
.RB [ \-\-programid
.IR id ]
.RB [ \-\-headers
.IR headers ]
.RB [ \-\-regionid
.IR id ]
.RB [ \-O | \-\-sort
.IR sort_fields ]
.RB [ \-S | \-\-select
.IR Selection ]
.RB [ \-\-units
.IR units ]
.RB [ \-\-nosuffix ]
.br
.ad b
.SH DESCRIPTION
The dmstats program manages IO statistics regions for devices that use
the device-mapper driver. Statistics regions may be created, deleted,
listed and reported on using the tool.
The first argument to dmstats is a command.
The second argument is the device name, uuid, or major and minor
numbers.
Further options permit the selection of regions, output format
control, and reporting behaviour.
When the program is run using the 'dmstats' alias, the command
\fBmust\fP be the first argument and any switches and options should be
specified following the command itself. This limitation is not present
when run as 'dmsetup stats'.
When no device argument is given dmstats will by default operate on all
device-mapper devices present. The \fBcreate\fP and \fBdelete\fP
commands require the use of \fB--force\fP when used in this way.
.SH OPTIONS
.TP
.B \-\-allprograms
Include regions from all program IDs for list and report operations.
.TP
.B \-\-allregions
Include all present regions for commands that normally accept a single
region identifier.
.TP
.B \-\-areas \fInr_areas
Specify the number of statistics areas to create within a new region.
.TP
.B \-\-areasize \fIarea_size
Specify the size of areas into which a new region should be divided. An
optional suffix selects units of bBsSkKmMgGtTpPeE: (b)ytes,
(s)ectors, (k)ilobytes, (m)egabytes, (g)igabytes, (t)erabytes,
(p)etabytes, (e)xabytes. Capitalise to use multiples of 1000 (S.I.)
instead of 1024.
.TP
.B \-\-auxdata \fIaux_data
Specify auxilliary data (a string) to be stored with a new region.
.TP
.B \-\-clear
When printing statistics counters also atomically reset them to zero.
.TP
.B \-\-count \fIcount
Specify the iteration count for repeating reports. If the count
argument is zero reports will continue to repeat until interrupted.
.TP
.B \-\-headers \fIheader_list
Specify which headers to display.
.TP
.B \-\-interval \fIinterval
Specify the interval, in seconds, between successive iterations for
repeating reports.
.TP
.B \-\-interval \fIseconds
Specify the interval in seconds between successive iterations for
repeating reports. If \-\-interval is specified but \-\-count is not,
reports will continue to repeat until interrupted.
.TP
.B \-\-length \fIlength
Specify the length of a new statistics region in sectors. An optional
suffix selects units of bBsSkKmMgGtTpPeE: (b)ytes, (s)ectors,
(k)ilobytes, (m)egabytes, (g)igabytes, (t)erabytes, (p)etabytes,
(e)xabytes. Capitalise to use multiples of 1000 (S.I.) instead of 1024.
.TP
.BR \-j | \-\-major\ \fImajor
Specify the major number.
.TP
.BR \-m | \-\-minor\ \fIminor
Specify the minor number.
.TP
.B \-\-nosuffix
Suppress the suffix on output sizes. Use with \fB\-\-units\fP
(except h and H) if processing the output.
.TP
.BR \-o | \-\-options
Specify which report fields to display.
.TP
.BR \-O | \-\-sort\ \fIsort_fields
Sort output according to the list of fields given. Precede any
sort_field with - for a reverse sort on that column.
.TP
.B \-\-programid \fIid
Specify a program ID string. When creating new statistics regions this
string is stored with the region. Subsequent operations may supply a
program ID in order to select only regions with a matching value. The
default program ID for dmstats-managed regions is "dmstats".
.TP
.BR \-S | \-\-select \ \fISelection
Display only rows that match Selection criteria. All rows with the
additional "selected" column (-o selected) showing 1 if the row matches
the Selection and 0 otherwise. The Selection criteria are defined by
specifying column names and their valid values while making use of
supported comparison operators.
.TP
.B \-\-start \fIstart
Specify the start offset of a new statistics region in sectors. An
optional suffix selects units of bBsSkKmMgGtTpPeE: (b)ytes,
(s)ectors, (k)ilobytes, (m)egabytes, (g)igabytes, (t)erabytes,
(p)etabytes, (e)xabytes. Capitalise to use multiples of 1000 (S.I.)
instead of 1024.
.TP
.B \-\-targets
Create a new statistics region for each target contained in the target
device. This causes a separate region to be allocated for each segment
of the device.
.TP
.B \-\-timestamps
Output a timestamp before each statistics report.
.TP
.BR \-\-units \ hHbBsSkKmMgGtTpPeE
Set the display units for report output. All sizes are output in these
units: (h)uman-readable, (b)ytes, (s)ectors, (k)ilobytes, (m)egabytes,
(g)igabytes, (t)erabytes, (p)etabytes, (e)xabytes. Capitalise to use
multiples of 1000 (S.I.) instead of 1024. Can also specify custom units
e.g. \fB\-\-units 3M\fP
.TP
.BR \-u | \-\-uuid
Specify the uuid.
.TP
.BR \-v | \-\-verbose \ [ \-v | \-\-verbose ]
Produce additional output.
.br
.SH COMMANDS
.TP
.B clear
.I device_name
.RB [ \-\-allregions
.RB | \-\-regionid
.IR id ]
.RB [ \-\-allprograms
.RB | \-\-programid
.IR id ]
.br
Instructs the kernel to clear statistics counters for the speficied
regions (with the exception of in-flight IO counters).
.br
.TP
.B create
.I device_name
.RB [ \-\-areas
.IR nr_areas ]
.RB [ \-\-areasize
.IR area_size ]
.RB [[ \-\-start
.IR start_sector ]
.RB [ \-\-length
.IR length ]
.RB |[ \-\-targets ]]
.RB [ \-\-auxdata
.IR data ]
.RB [ \-\-programid
.IR id ]
.br
Creates one or more new statistics regions on the specified device(s).
The region will span the entire device unless \fB\-\-start\fP and
\fB\-\-length\fP or \fB\-\-target\fP are given. The \fB\-\-start\fP and
\fB\-\-length\fP options allow a region of arbitrary length to be placed
at an arbitrary offset into the device. The \fB\-\-targets\fP option
causes a new region to be created for each target in the corresponding
device-mapper device's table.
An optional \fBprogram_id\fP or \fBaux_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
stores an arbitrary string and is not used by dmstats or the
device-mapper kernel statistics subsystem.
By default dmstats creates regions with a \fBprogram_id\fP of
"DMSTATS1".
On success the \fBregion_id\fP of the newly created region is printed to
stdout.
.br
.TP
.B delete
.I [ device_name ]
.RB [ \-\-force ]
.RB [ \-\-allregions
.RB | \-\-regionid
.IR id ]
.RB [ \-\-allprograms
.RB | \-\-programid
.IR id ]
.br
Delete the specified statistics region. All counters and resources used
by the region are released and the region will not appear in the output
of subsequent list, print, or report operations.
All regions registered on a device may be removed using
\fB\-\-allregions\fP.
To remove all regions on all devices \fB\-\-force\fP must be used.
.br
.TP
.B help
.RB [ \-c | \-C | \-\-columns ]
.br
Outputs a summary of the commands available, optionally including
the list of report fields.
.br
.TP
.B list
.RI [ device_name ]
.RB [ \-\-allprograms ]
.RB [ \-\-programid
.IR id ]
.br
List the statistics regions registered on the device. If the
\fB\-\-allprograms\fP switch is given all regions will be listed
regardless of region program ID values.
.br
.TP
.B print
.RB [ \-\-clear ]
.IR
.RB [ \-\-allregions
.RB | \-\-regionid
.IR id ]
.RB [ \-\-allprograms
.RB | \-\-programid
.IR id ]
.br
Print raw statistics counters for the specified region or for all
present regions.
.br
.TP
.B report
.RB [ \-\-allprograms ]
.RB [ \-\-interval
.IR seconds ]
.RB [ \-\-headers
.IR headers ]
.RB [ \-\-count
.IR count ]
.RB [ \-\-timestamps ]
.RB [ \-\-units
.IR unit ]
.RB [ \-\-regionid
.IR id ]
.RB [ \-\-programid
.IR id ]
.RB [ \-O | \-\-sort
.IR sort_fields ]
.RB [ \-S | \-\-select
.IR Selection ]
.RB [ \-\-units
.IR units ]
.br
Start a report for the specified region or for all present regions. 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.
If the \fB\-\-allprograms\fP switch is given, all regions will be
listed, regardless of region program ID values.
A header row is optionally printed before each statistics report is
displayed. The list of headers to include is specified using the
\fB\-\-headers\fP option.
.br
.SH REGIONS AND AREAS
The device-mapper statistics facility allows separate performance
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.
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.
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
\fBcreate\fP command. Depending on the size of the areas and the device
region the final area within the region may be smaller than requested.
.SS Region identifiers
Each region is assigned an identifier when it is created that is used to
reference the region in subsequent operations. Region identifiers are
unique within a given device (including across different \fBprogram_id\fP
values).
.br
Depending on the sequence of create and delete operations gaps, may
exist in the sequence of \fBregion_id\fP values for a particular device.
.SH REPORT FIELDS
The dmstats report provides several types of field that may be added to
the default field set, or used to create custom reports.
.br
All performance counters and metrics are calculated per-area.
.br
.SS Derived metrics
A number of metrics fields are included that provide high level
performance indicators. These are based on the fields provided by the
conventional Linux iostat program and are derived from the basic counter
values provided by the kernel for each area.
.br
.HP
.B rrqm
.br
Read requests merged per second.
.HP
.B wrqm
.br
Write requests merged per second.
.HP
.B rs
.br
Read requests per second.
.HP
.B ws
.br
Write requests per second.
.HP
.B rsec
.br
Sectors read per second.
.HP
.B wsec
Sectors written per second.
.HP
.B arqsz
.br
The average size of requests submitted to the area.
.HP
.B qusz
.br
The average queue length.
.HP
.B await
.br
The average wait time for read and write requests.
.HP
.B r_await
.br
The average wait time for read requests.
.HP
.B w_await
.br
The average wait time for write requests.
.HP
.B tput
.br
The device throughput in requests per second.
.HP
.B svctm
The average service time (in milliseconds) for I/O requests that
were issued to the device.
.HP
.B util
.br
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%.
.br
.SS Region and area meta fields
Meta fields provide information about the region or area that the
statistics values relate to. This includes the region and area
identifier, start, length, and counts as well as the program ID and
auxiliary data values.
.br
.HP
.B region_id
.br
Region identifier. This is a non-negative integer returned by the kernel
when a statistics region is created.
.HP
.B region_start
.br
.br
The region start sector in units of 512 byte sectors.
.HP
.B region_len
.br
The length of the region in units of 512 byte sectors.
.HP
.B area_id
.br
Area identifier. Area identifiers are assigned by the device-mapper
statistics library and uniquely identify each area within a region. Each
ID corresponds to a distinct set of performance counters for that area
of the statistics region. Area identifiers are always monotonically
increasing within a region so that higher ID values correspond to
greater sector addresses within the region and no gaps in the sequence
of identifiers exist. Sorting a report by device, region start, and area
ID (the default) will then produce rows in order of ascending region and
area address.
.HP
.B area_start
.br
The area start sector in units of 512 byte sectors.
.HP
.B area_len
.br
The length of the area in units of 512 byte sectors.
.HP
.B area_count
The number of areas in this region.
.HP
.B program_id
.br
The program ID value associated with this region.
.HP
.B aux_data
.br
The auxiliary data value associated with this region.
.br
.SS Basic counters
Basic counters provide access to the raw counter data from the kernel,
allowing further processing to be carried out by another program.
The kernel provides thirteen separate counters for each statistics
area. The first eleven of these match the counters provided in
/proc/diskstats or /sys/block/*/*/stat. The final pair provide separate
counters for read and write time.
.P
.HP
.B reads
.br
The number of reads successfully completed this interval.
.HP
.B read_merges
.br
The number of read requests merged this interval. This field is
incremented every time a pair of requests are merged to create a single
request to be issued to the device.
.HP
.B read_sectors
.br
The number of 512 byte sectors read this interval.
.HP
.B read_nsecs
.br
The number of nanoseconds spent reading during this interval.
.HP
.B writes
.br
The number of writes successfully completed this interval.
.HP
.B write_merges
.br
The number of write requests merged this interval. This field is
incremented every time a pair of requests are merged to create a single
request to be issued to the device.
.HP
.B write_sectors
.br
The number of 512 byte sectors written this interval.
.HP
.B write_nsecs
.br
The number of nanoseconds spent writing during this interval.
.HP
.B in_progress
.br
The number of reads and writes currently in progress.
.HP
.B io_nsecs
.br
The number of nanoseconds spent reading and writing.
.HP
.B weighted_io_nsecs
.br
This field is incremented at each I/O start, I/O completion, I/O merge,
or read of these stats by the number of I/Os in progress times the
number of milliseconds spent doing I/O since the last update of this
field. This can provide an easy measure of both I/O completion time and
the backlog that may be accumulating.
.br
.br
.P
.SH EXAMPLES
Create a whole-device region with one area on vg_hex-lv_root
.br
.br
# dmstats create vg_hex-lv_root
.br
Created region: 0
.br
.br
Create a 32M region 1G into device d0
.br
.br
# dmstats create --start 1G --length 32M d0
.br
Created region: 2
.br
Create a whole-device region with 8 areas on every device
.br
.br
# dmstats create --areas 8
.br
Created region: 0
.br
Created region: 0
.br
Created region: 0
.br
Created region: 2
.br
Created region: 0
.br
Created region: 0
.br
.br
Delete all regions on all devices
.br
.br
# dmstats delete --allregions --force
.br
.br
Create a whole-device region with areas 10GiB in size on vg_hex-lv_root
using dmsetup
.br
.br
# dmsetup stats create --areasize 10G vg_hex-lv_root
.br
Created region: 1
.br
.br
Create a 1GiB region with 16 areas at the start of vg_hex-lv_root
.br
# dmstats create --start 0 --len 1G --areas=16 vg_hex-lv_root
.br
Created region: 2
.br
.br
List the statistics regions registered on vg_hex-lv_root
.br
# dmstats list vg_hex-lv_root
.br
RegionID RegStart RegLen AreaSize ProgramID AuxData
.br
0 0 104857600 20971520 dmstats
.br
1 0 104857600 20971520 dmstats
.br
2 0 2097152 131072 dmstats
.br
.br
Display five statistics reports, with timestamps, for vg_hex-lv_root at an interval of one second
.br
.br
# dmstats report --time --interval 1 --count 5
.br
21/07/15 21:04:26
.br
Name RgID ArID RRqM/s WRqM/s R/s W/s RSz/s WSz/s AvRqSz QSize SvcTm Util% AWait
.br
vg_hex-lv_root 0 0 0.00 0.00 8.00 0.00 48.00k 0 6.00k 0.00 5.50 4.40 6.62
.br
vg_hex-lv_root 0 1 0.00 0.00 22.00 0.00 624.00k 0 28.00k 0.00 5.23 11.50 5.36
.br
vg_hex-lv_root 0 2 0.00 0.00 353.00 0.00 1.84m 0 5.00k 0.00 1.34 47.40 1.33
.br
vg_hex-lv_root 0 3 0.00 0.00 73.00 0.00 592.00k 0 8.00k 0.00 2.10 15.30 2.10
.br
vg_hex-lv_root 0 4 0.00 0.00 5.00 0.00 52.00k 0 10.00k 0.00 4.00 2.00 4.00
.br
[...]
.br
.br
Create one region for reach target contained in device vg_hex-lv_home
.br
.br
# dmstats create --targets vg_hex-lv_home
.br
Created region: 0
.br
Created region: 1
.br
Created region: 2
.br
.br
Print raw counters for region 4 on device d0
.br
.br
# dmstats print --regionid 4 d0
.br
2097152+65536 0 0 0 0 29 0 264 701 0 41 701 0 41
.br
.br
.SH AUTHORS
Bryn M. Reeves <bmr@redhat.com>
.SH SEE ALSO
LVM2 resource page https://www.sourceware.org/lvm2/
.br
Device-mapper resource page: http://sources.redhat.com/dm/
.br
Device-mapper statistics kernel documentation
.br
Documentation/device-mapper/statistics.txt

File diff suppressed because it is too large Load Diff