block: Compute minimum, maximum and average I/O latencies
This patch keeps track of the minimum, maximum and average latencies of I/O operations during a certain interval of time. The values are exposed in the BlockDeviceTimedStats structure. An option to define the intervals to collect these statistics will be added in a separate patch. Signed-off-by: Alberto Garcia <berto@igalia.com> Message-id: c7382dc89622c64f918d09f32815827772628f8e.1446044837.git.berto@igalia.com Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
This commit is contained in:
parent
362e9299b3
commit
979e9b03fc
@ -35,6 +35,39 @@ void block_acct_init(BlockAcctStats *stats, bool account_invalid,
|
||||
stats->account_failed = account_failed;
|
||||
}
|
||||
|
||||
void block_acct_cleanup(BlockAcctStats *stats)
|
||||
{
|
||||
BlockAcctTimedStats *s, *next;
|
||||
QSLIST_FOREACH_SAFE(s, &stats->intervals, entries, next) {
|
||||
g_free(s);
|
||||
}
|
||||
}
|
||||
|
||||
void block_acct_add_interval(BlockAcctStats *stats, unsigned interval_length)
|
||||
{
|
||||
BlockAcctTimedStats *s;
|
||||
unsigned i;
|
||||
|
||||
s = g_new0(BlockAcctTimedStats, 1);
|
||||
s->interval_length = interval_length;
|
||||
QSLIST_INSERT_HEAD(&stats->intervals, s, entries);
|
||||
|
||||
for (i = 0; i < BLOCK_MAX_IOTYPE; i++) {
|
||||
timed_average_init(&s->latency[i], clock_type,
|
||||
(uint64_t) interval_length * NANOSECONDS_PER_SECOND);
|
||||
}
|
||||
}
|
||||
|
||||
BlockAcctTimedStats *block_acct_interval_next(BlockAcctStats *stats,
|
||||
BlockAcctTimedStats *s)
|
||||
{
|
||||
if (s == NULL) {
|
||||
return QSLIST_FIRST(&stats->intervals);
|
||||
} else {
|
||||
return QSLIST_NEXT(s, entries);
|
||||
}
|
||||
}
|
||||
|
||||
void block_acct_start(BlockAcctStats *stats, BlockAcctCookie *cookie,
|
||||
int64_t bytes, enum BlockAcctType type)
|
||||
{
|
||||
@ -47,6 +80,7 @@ void block_acct_start(BlockAcctStats *stats, BlockAcctCookie *cookie,
|
||||
|
||||
void block_acct_done(BlockAcctStats *stats, BlockAcctCookie *cookie)
|
||||
{
|
||||
BlockAcctTimedStats *s;
|
||||
int64_t time_ns = qemu_clock_get_ns(clock_type);
|
||||
int64_t latency_ns = time_ns - cookie->start_time_ns;
|
||||
|
||||
@ -56,6 +90,10 @@ void block_acct_done(BlockAcctStats *stats, BlockAcctCookie *cookie)
|
||||
stats->nr_ops[cookie->type]++;
|
||||
stats->total_time_ns[cookie->type] += latency_ns;
|
||||
stats->last_access_time_ns = time_ns;
|
||||
|
||||
QSLIST_FOREACH(s, &stats->intervals, entries) {
|
||||
timed_average_account(&s->latency[cookie->type], latency_ns);
|
||||
}
|
||||
}
|
||||
|
||||
void block_acct_failed(BlockAcctStats *stats, BlockAcctCookie *cookie)
|
||||
@ -65,11 +103,16 @@ void block_acct_failed(BlockAcctStats *stats, BlockAcctCookie *cookie)
|
||||
stats->failed_ops[cookie->type]++;
|
||||
|
||||
if (stats->account_failed) {
|
||||
BlockAcctTimedStats *s;
|
||||
int64_t time_ns = qemu_clock_get_ns(clock_type);
|
||||
int64_t latency_ns = time_ns - cookie->start_time_ns;
|
||||
|
||||
stats->total_time_ns[cookie->type] += latency_ns;
|
||||
stats->last_access_time_ns = time_ns;
|
||||
|
||||
QSLIST_FOREACH(s, &stats->intervals, entries) {
|
||||
timed_average_account(&s->latency[cookie->type], latency_ns);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -176,6 +176,7 @@ static void blk_delete(BlockBackend *blk)
|
||||
}
|
||||
g_free(blk->name);
|
||||
drive_info_del(blk->legacy_dinfo);
|
||||
block_acct_cleanup(&blk->stats);
|
||||
g_free(blk);
|
||||
}
|
||||
|
||||
|
28
block/qapi.c
28
block/qapi.c
@ -346,6 +346,7 @@ static BlockStats *bdrv_query_stats(const BlockDriverState *bs,
|
||||
s->stats = g_malloc0(sizeof(*s->stats));
|
||||
if (bs->blk) {
|
||||
BlockAcctStats *stats = blk_get_stats(bs->blk);
|
||||
BlockAcctTimedStats *ts = NULL;
|
||||
|
||||
s->stats->rd_bytes = stats->nr_bytes[BLOCK_ACCT_READ];
|
||||
s->stats->wr_bytes = stats->nr_bytes[BLOCK_ACCT_WRITE];
|
||||
@ -375,6 +376,33 @@ static BlockStats *bdrv_query_stats(const BlockDriverState *bs,
|
||||
|
||||
s->stats->account_invalid = stats->account_invalid;
|
||||
s->stats->account_failed = stats->account_failed;
|
||||
|
||||
while ((ts = block_acct_interval_next(stats, ts))) {
|
||||
BlockDeviceTimedStatsList *timed_stats =
|
||||
g_malloc0(sizeof(*timed_stats));
|
||||
BlockDeviceTimedStats *dev_stats = g_malloc0(sizeof(*dev_stats));
|
||||
timed_stats->next = s->stats->timed_stats;
|
||||
timed_stats->value = dev_stats;
|
||||
s->stats->timed_stats = timed_stats;
|
||||
|
||||
TimedAverage *rd = &ts->latency[BLOCK_ACCT_READ];
|
||||
TimedAverage *wr = &ts->latency[BLOCK_ACCT_WRITE];
|
||||
TimedAverage *fl = &ts->latency[BLOCK_ACCT_FLUSH];
|
||||
|
||||
dev_stats->interval_length = ts->interval_length;
|
||||
|
||||
dev_stats->min_rd_latency_ns = timed_average_min(rd);
|
||||
dev_stats->max_rd_latency_ns = timed_average_max(rd);
|
||||
dev_stats->avg_rd_latency_ns = timed_average_avg(rd);
|
||||
|
||||
dev_stats->min_wr_latency_ns = timed_average_min(wr);
|
||||
dev_stats->max_wr_latency_ns = timed_average_max(wr);
|
||||
dev_stats->avg_wr_latency_ns = timed_average_avg(wr);
|
||||
|
||||
dev_stats->min_flush_latency_ns = timed_average_min(fl);
|
||||
dev_stats->max_flush_latency_ns = timed_average_max(fl);
|
||||
dev_stats->avg_flush_latency_ns = timed_average_avg(fl);
|
||||
}
|
||||
}
|
||||
|
||||
s->stats->wr_highest_offset = bs->wr_highest_offset;
|
||||
|
@ -28,6 +28,9 @@
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "qemu/typedefs.h"
|
||||
#include "qemu/timed-average.h"
|
||||
|
||||
typedef struct BlockAcctTimedStats BlockAcctTimedStats;
|
||||
|
||||
enum BlockAcctType {
|
||||
BLOCK_ACCT_READ,
|
||||
@ -36,6 +39,12 @@ enum BlockAcctType {
|
||||
BLOCK_MAX_IOTYPE,
|
||||
};
|
||||
|
||||
struct BlockAcctTimedStats {
|
||||
TimedAverage latency[BLOCK_MAX_IOTYPE];
|
||||
unsigned interval_length; /* in seconds */
|
||||
QSLIST_ENTRY(BlockAcctTimedStats) entries;
|
||||
};
|
||||
|
||||
typedef struct BlockAcctStats {
|
||||
uint64_t nr_bytes[BLOCK_MAX_IOTYPE];
|
||||
uint64_t nr_ops[BLOCK_MAX_IOTYPE];
|
||||
@ -44,6 +53,7 @@ typedef struct BlockAcctStats {
|
||||
uint64_t total_time_ns[BLOCK_MAX_IOTYPE];
|
||||
uint64_t merged[BLOCK_MAX_IOTYPE];
|
||||
int64_t last_access_time_ns;
|
||||
QSLIST_HEAD(, BlockAcctTimedStats) intervals;
|
||||
bool account_invalid;
|
||||
bool account_failed;
|
||||
} BlockAcctStats;
|
||||
@ -56,6 +66,10 @@ typedef struct BlockAcctCookie {
|
||||
|
||||
void block_acct_init(BlockAcctStats *stats, bool account_invalid,
|
||||
bool account_failed);
|
||||
void block_acct_cleanup(BlockAcctStats *stats);
|
||||
void block_acct_add_interval(BlockAcctStats *stats, unsigned interval_length);
|
||||
BlockAcctTimedStats *block_acct_interval_next(BlockAcctStats *stats,
|
||||
BlockAcctTimedStats *s);
|
||||
void block_acct_start(BlockAcctStats *stats, BlockAcctCookie *cookie,
|
||||
int64_t bytes, enum BlockAcctType type);
|
||||
void block_acct_done(BlockAcctStats *stats, BlockAcctCookie *cookie);
|
||||
|
@ -414,6 +414,52 @@
|
||||
##
|
||||
{ 'command': 'query-block', 'returns': ['BlockInfo'] }
|
||||
|
||||
|
||||
##
|
||||
# @BlockDeviceTimedStats:
|
||||
#
|
||||
# Statistics of a block device during a given interval of time.
|
||||
#
|
||||
# @interval_length: Interval used for calculating the statistics,
|
||||
# in seconds.
|
||||
#
|
||||
# @min_rd_latency_ns: Minimum latency of read operations in the
|
||||
# defined interval, in nanoseconds.
|
||||
#
|
||||
# @min_wr_latency_ns: Minimum latency of write operations in the
|
||||
# defined interval, in nanoseconds.
|
||||
#
|
||||
# @min_flush_latency_ns: Minimum latency of flush operations in the
|
||||
# defined interval, in nanoseconds.
|
||||
#
|
||||
# @max_rd_latency_ns: Maximum latency of read operations in the
|
||||
# defined interval, in nanoseconds.
|
||||
#
|
||||
# @max_wr_latency_ns: Maximum latency of write operations in the
|
||||
# defined interval, in nanoseconds.
|
||||
#
|
||||
# @max_flush_latency_ns: Maximum latency of flush operations in the
|
||||
# defined interval, in nanoseconds.
|
||||
#
|
||||
# @avg_rd_latency_ns: Average latency of read operations in the
|
||||
# defined interval, in nanoseconds.
|
||||
#
|
||||
# @avg_wr_latency_ns: Average latency of write operations in the
|
||||
# defined interval, in nanoseconds.
|
||||
#
|
||||
# @avg_flush_latency_ns: Average latency of flush operations in the
|
||||
# defined interval, in nanoseconds.
|
||||
#
|
||||
# Since: 2.5
|
||||
##
|
||||
|
||||
{ 'struct': 'BlockDeviceTimedStats',
|
||||
'data': { 'interval_length': 'int', 'min_rd_latency_ns': 'int',
|
||||
'max_rd_latency_ns': 'int', 'avg_rd_latency_ns': 'int',
|
||||
'min_wr_latency_ns': 'int', 'max_wr_latency_ns': 'int',
|
||||
'avg_wr_latency_ns': 'int', 'min_flush_latency_ns': 'int',
|
||||
'max_flush_latency_ns': 'int', 'avg_flush_latency_ns': 'int' } }
|
||||
|
||||
##
|
||||
# @BlockDeviceStats:
|
||||
#
|
||||
@ -476,6 +522,9 @@
|
||||
# @account_failed: Whether failed operations are included in the
|
||||
# latency and last access statistics (Since 2.5)
|
||||
#
|
||||
# @timed_stats: Statistics specific to the set of previously defined
|
||||
# intervals of time (Since 2.5)
|
||||
#
|
||||
# Since: 0.14.0
|
||||
##
|
||||
{ 'struct': 'BlockDeviceStats',
|
||||
@ -487,7 +536,8 @@
|
||||
'failed_rd_operations': 'int', 'failed_wr_operations': 'int',
|
||||
'failed_flush_operations': 'int', 'invalid_rd_operations': 'int',
|
||||
'invalid_wr_operations': 'int', 'invalid_flush_operations': 'int',
|
||||
'account_invalid': 'bool', 'account_failed': 'bool' } }
|
||||
'account_invalid': 'bool', 'account_failed': 'bool',
|
||||
'timed_stats': ['BlockDeviceTimedStats'] } }
|
||||
|
||||
##
|
||||
# @BlockStats:
|
||||
|
@ -2604,6 +2604,37 @@ Each json-object contain the following:
|
||||
- "account_failed": whether failed operations are included in the
|
||||
latency and last access statistics
|
||||
(json-bool)
|
||||
- "timed_stats": A json-array containing statistics collected in
|
||||
specific intervals, with the following members:
|
||||
- "interval_length": interval used for calculating the
|
||||
statistics, in seconds (json-int)
|
||||
- "min_rd_latency_ns": minimum latency of read operations in
|
||||
the defined interval, in nanoseconds
|
||||
(json-int)
|
||||
- "min_wr_latency_ns": minimum latency of write operations in
|
||||
the defined interval, in nanoseconds
|
||||
(json-int)
|
||||
- "min_flush_latency_ns": minimum latency of flush operations
|
||||
in the defined interval, in
|
||||
nanoseconds (json-int)
|
||||
- "max_rd_latency_ns": maximum latency of read operations in
|
||||
the defined interval, in nanoseconds
|
||||
(json-int)
|
||||
- "max_wr_latency_ns": maximum latency of write operations in
|
||||
the defined interval, in nanoseconds
|
||||
(json-int)
|
||||
- "max_flush_latency_ns": maximum latency of flush operations
|
||||
in the defined interval, in
|
||||
nanoseconds (json-int)
|
||||
- "avg_rd_latency_ns": average latency of read operations in
|
||||
the defined interval, in nanoseconds
|
||||
(json-int)
|
||||
- "avg_wr_latency_ns": average latency of write operations in
|
||||
the defined interval, in nanoseconds
|
||||
(json-int)
|
||||
- "avg_flush_latency_ns": average latency of flush operations
|
||||
in the defined interval, in
|
||||
nanoseconds (json-int)
|
||||
- "parent": Contains recursively the statistics of the underlying
|
||||
protocol (e.g. the host file for a qcow2 image). If there is
|
||||
no underlying protocol, this field is omitted
|
||||
|
Loading…
Reference in New Issue
Block a user