All of lore.kernel.org
 help / color / mirror / Atom feed
From: Stefan Hajnoczi <stefanha@redhat.com>
To: qemu-devel@nongnu.org
Cc: Peter Maydell <peter.maydell@linaro.org>,
	Alberto Garcia <berto@igalia.com>,
	Stefan Hajnoczi <stefanha@redhat.com>
Subject: [Qemu-devel] [PULL 31/44] block: Compute minimum, maximum and average I/O latencies
Date: Tue, 10 Nov 2015 14:14:26 +0000	[thread overview]
Message-ID: <1447164879-6756-32-git-send-email-stefanha@redhat.com> (raw)
In-Reply-To: <1447164879-6756-1-git-send-email-stefanha@redhat.com>

From: Alberto Garcia <berto@igalia.com>

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>
---
 block/accounting.c         | 43 ++++++++++++++++++++++++++++++++++++++
 block/block-backend.c      |  1 +
 block/qapi.c               | 28 +++++++++++++++++++++++++
 include/block/accounting.h | 14 +++++++++++++
 qapi/block-core.json       | 52 +++++++++++++++++++++++++++++++++++++++++++++-
 qmp-commands.hx            | 31 +++++++++++++++++++++++++++
 6 files changed, 168 insertions(+), 1 deletion(-)

diff --git a/block/accounting.c b/block/accounting.c
index 923aeaf..61de8ce 100644
--- a/block/accounting.c
+++ b/block/accounting.c
@@ -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);
+        }
     }
 }
 
diff --git a/block/block-backend.c b/block/block-backend.c
index 19fdaae..fd4f895 100644
--- a/block/block-backend.c
+++ b/block/block-backend.c
@@ -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);
 }
 
diff --git a/block/qapi.c b/block/qapi.c
index 56c8139..4baf6e1 100644
--- a/block/qapi.c
+++ b/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;
diff --git a/include/block/accounting.h b/include/block/accounting.h
index 0d9b076..1dd582a 100644
--- a/include/block/accounting.h
+++ b/include/block/accounting.h
@@ -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);
diff --git a/qapi/block-core.json b/qapi/block-core.json
index b33663b..e2cfac6 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -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:
diff --git a/qmp-commands.hx b/qmp-commands.hx
index 70cfea5..e9b3a76 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -2547,6 +2547,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
-- 
2.5.0

  parent reply	other threads:[~2015-11-10 14:16 UTC|newest]

Thread overview: 50+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-11-10 14:13 [Qemu-devel] [PULL 00/44] Block patches Stefan Hajnoczi
2015-11-10 14:13 ` [Qemu-devel] [PULL 01/44] block: Add more types for tracked request Stefan Hajnoczi
2015-11-10 14:13 ` [Qemu-devel] [PULL 02/44] block: Track flush requests Stefan Hajnoczi
2015-11-10 14:13 ` [Qemu-devel] [PULL 03/44] block: Track discard requests Stefan Hajnoczi
2015-11-10 14:13 ` [Qemu-devel] [PULL 04/44] iscsi: Emulate commands in iscsi_aio_ioctl as iscsi_ioctl Stefan Hajnoczi
2015-11-10 14:14 ` [Qemu-devel] [PULL 05/44] block: Add ioctl parameter fields to BlockRequest Stefan Hajnoczi
2015-11-10 14:14 ` [Qemu-devel] [PULL 06/44] block: Emulate bdrv_ioctl with bdrv_aio_ioctl and track both Stefan Hajnoczi
2015-11-10 14:14 ` [Qemu-devel] [PULL 07/44] block: Drop BlockDriver.bdrv_ioctl Stefan Hajnoczi
2015-11-10 14:14 ` [Qemu-devel] [PULL 08/44] block: Introduce BlockDriver.bdrv_drain callback Stefan Hajnoczi
2015-11-10 14:14 ` [Qemu-devel] [PULL 09/44] qed: Implement .bdrv_drain Stefan Hajnoczi
2015-11-10 14:14 ` [Qemu-devel] [PULL 10/44] qapi: Add transaction support to block-dirty-bitmap operations Stefan Hajnoczi
2015-11-10 14:14 ` [Qemu-devel] [PULL 11/44] iotests: add transactional incremental backup test Stefan Hajnoczi
2015-11-10 14:14 ` [Qemu-devel] [PULL 12/44] block: rename BlkTransactionState and BdrvActionOps Stefan Hajnoczi
2015-11-10 14:14 ` [Qemu-devel] [PULL 13/44] backup: Extract dirty bitmap handling as a separate function Stefan Hajnoczi
2015-11-10 14:14 ` [Qemu-devel] [PULL 14/44] blockjob: Introduce reference count and fix reference to job->bs Stefan Hajnoczi
2015-11-10 14:14 ` [Qemu-devel] [PULL 15/44] blockjob: Add .commit and .abort block job actions Stefan Hajnoczi
2015-11-10 14:14 ` [Qemu-devel] [PULL 16/44] blockjob: Add "completed" and "ret" in BlockJob Stefan Hajnoczi
2015-11-10 14:14 ` [Qemu-devel] [PULL 17/44] blockjob: Simplify block_job_finish_sync Stefan Hajnoczi
2015-11-10 14:14 ` [Qemu-devel] [PULL 18/44] block: Add block job transactions Stefan Hajnoczi
2015-11-10 14:14 ` [Qemu-devel] [PULL 19/44] block/backup: Rely on commit/abort for cleanup Stefan Hajnoczi
2015-11-10 14:14 ` [Qemu-devel] [PULL 20/44] block: Add BlockJobTxn support to backup_run Stefan Hajnoczi
2015-11-10 14:14 ` [Qemu-devel] [PULL 21/44] block: add transactional properties Stefan Hajnoczi
2015-11-10 14:14 ` [Qemu-devel] [PULL 22/44] iotests: 124 - transactional failure test Stefan Hajnoczi
2015-11-10 14:14 ` [Qemu-devel] [PULL 23/44] tests: add BlockJobTxn unit test Stefan Hajnoczi
2015-11-12 18:26   ` Eric Blake
2015-11-10 14:14 ` [Qemu-devel] [PULL 24/44] xen_disk: Account for flush operations Stefan Hajnoczi
2015-11-10 14:14 ` [Qemu-devel] [PULL 25/44] ide: Account for write operations correctly Stefan Hajnoczi
2015-11-10 14:14 ` [Qemu-devel] [PULL 26/44] block: define 'clock_type' for the accounting code Stefan Hajnoczi
2015-11-10 14:14 ` [Qemu-devel] [PULL 27/44] util: Infrastructure for computing recent averages Stefan Hajnoczi
2015-11-10 14:14 ` [Qemu-devel] [PULL 28/44] block: Add idle_time_ns to BlockDeviceStats Stefan Hajnoczi
2015-11-10 14:14 ` [Qemu-devel] [PULL 29/44] block: Add statistics for failed and invalid I/O operations Stefan Hajnoczi
2015-11-10 14:14 ` [Qemu-devel] [PULL 30/44] block: Allow configuring whether to account failed and invalid ops Stefan Hajnoczi
2015-11-10 14:14 ` Stefan Hajnoczi [this message]
2015-11-10 14:14 ` [Qemu-devel] [PULL 32/44] block: Add average I/O queue depth to BlockDeviceTimedStats Stefan Hajnoczi
2015-11-10 14:14 ` [Qemu-devel] [PULL 33/44] block: New option to define the intervals for collecting I/O statistics Stefan Hajnoczi
2015-11-10 17:23   ` Eric Blake
2015-11-10 18:49     ` Markus Armbruster
2015-11-11 11:10     ` Alberto Garcia
2015-11-10 14:14 ` [Qemu-devel] [PULL 34/44] qemu-io: Account for failed, invalid and flush operations Stefan Hajnoczi
2015-11-10 14:14 ` [Qemu-devel] [PULL 35/44] block: Use QEMU_CLOCK_VIRTUAL for the accounting code in qtest mode Stefan Hajnoczi
2015-11-10 15:08   ` Paolo Bonzini
2015-11-10 14:14 ` [Qemu-devel] [PULL 36/44] iotests: Add test for the block device statistics Stefan Hajnoczi
2015-11-10 14:14 ` [Qemu-devel] [PULL 37/44] nvme: Account for failed and invalid operations Stefan Hajnoczi
2015-11-10 14:14 ` [Qemu-devel] [PULL 38/44] virtio-blk: " Stefan Hajnoczi
2015-11-10 14:14 ` [Qemu-devel] [PULL 39/44] xen_disk: " Stefan Hajnoczi
2015-11-10 14:14 ` [Qemu-devel] [PULL 40/44] atapi: " Stefan Hajnoczi
2015-11-10 14:14 ` [Qemu-devel] [PULL 41/44] ide: " Stefan Hajnoczi
2015-11-10 14:14 ` [Qemu-devel] [PULL 42/44] macio: Account for failed operations Stefan Hajnoczi
2015-11-10 14:14 ` [Qemu-devel] [PULL 43/44] scsi-disk: " Stefan Hajnoczi
2015-11-10 14:14 ` [Qemu-devel] [PULL 44/44] block: Update copyright of the accounting code Stefan Hajnoczi

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1447164879-6756-32-git-send-email-stefanha@redhat.com \
    --to=stefanha@redhat.com \
    --cc=berto@igalia.com \
    --cc=peter.maydell@linaro.org \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.