mirror of
				git://sourceware.org/git/lvm2.git
				synced 2025-10-30 20:23:49 +03:00 
			
		
		
		
	Compare commits
	
		
			15 Commits
		
	
	
		
			v2_03_01
			...
			dev-bmr-dm
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 63e6ef8491 | ||
|  | a20989174f | ||
|  | 8df54e25c6 | ||
|  | 8ca04e316e | ||
|  | e8eed522a8 | ||
|  | cd8e3e28e5 | ||
|  | 8cc8b30ba7 | ||
|  | 1638e090fc | ||
|  | e33656fc53 | ||
|  | 13d5b8100a | ||
|  | 077afe5b23 | ||
|  | aa4d97d318 | ||
|  | a4b5f8ef39 | ||
|  | 795c590980 | ||
|  | ba2da29c4c | 
| @@ -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 | ||||
|   | ||||
| @@ -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 \ | ||||
|   | ||||
| @@ -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 | ||||
|  *************************/ | ||||
|   | ||||
| @@ -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
									
								
							
							
						
						
									
										1365
									
								
								libdm/libdm-stats.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -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
									
								
							
							
						
						
									
										712
									
								
								man/dmstats.8.in
									
									
									
									
									
										Normal 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 | ||||
							
								
								
									
										1936
									
								
								tools/dmsetup.c
									
									
									
									
									
								
							
							
						
						
									
										1936
									
								
								tools/dmsetup.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
		Reference in New Issue
	
	Block a user