* [Qemu-devel] [PATCH v4 00/21] Extended I/O accounting
@ 2015-10-28 15:32 Alberto Garcia
2015-10-28 15:32 ` [Qemu-devel] [PATCH v4 01/21] xen_disk: Account for flush operations Alberto Garcia
` (21 more replies)
0 siblings, 22 replies; 24+ messages in thread
From: Alberto Garcia @ 2015-10-28 15:32 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Alberto Garcia, qemu-block, Markus Armbruster,
Max Reitz, Stefan Hajnoczi
Here's v4 of the series that implements extended I/O accounting for
block devices.
Since part of Max's BlockBackend series has already been merged, this
series can now be applied cleanly on top of the master branch without
additional dependencies.
Here's the summary of what this series provides:
- New block_acct_failed() and block_acct_invalid() calls.
We keep track now of the number of successful, failed and invalid
operations (each one separated into read, write and flush). So from
the API point of view, BlockDeviceStats contains 6 new fields for
those.
- idle_time_ns: time since the last I/O operation.
- New BlockDeviceTimedStats struct: it has statistics for the I/O
during a given interval of time. It keeps minimum, maximum and
average latencies for read, write and flush operations.
It also keeps the average read and write queue depths.
- New 'stats-intervals' option that allows the user to define the
intervals used to keep the aforementioned statistics. An arbitrary
number of intervals can be specified, the length of each one is in
seconds.
For the API I opted for a colon-separated list of numbers,
stats-intervals=60:3600:86400
I also considered something a different syntax,
stats-intervals.0.length=60,
stats-intervals.1.length=3600,
stats-intervals.2.length=86400
This one could be useful if we want to specify any other attribute
for each interval, but I couldn't come up with any, so I chose the
simpler solution.
- Two new options, stats-account-invalid and stats-account-failed,
which allow the user to decide whether to count invalid and failed
operations when computing the idle time and total latency.
Regards,
Berto
v4:
- Rebase on top of the current master. This series no longer depends
on any other.
- patch 8: clarify that interval_length is in seconds [Stefan]
- patch 9: rewrite timed_average_sum() so it does not call
qemu_clock_get_ns() twice [Stefan]
v3: https://lists.gnu.org/archive/html/qemu-block/2015-10/msg00785.html
- Rebased on top of the current master and on Max's BlockBackend
series v7
- patch 4: minor documentation fixes [Stefan]
- patch 5: s/miliseconds/nanoseconds/ [Stefan]
- patch 6: dropped, there's no "supports_stats" anymore [Stefan]
- patch 7 (now 6): explain why block_acct_invalid() does not update
total_time_ns[] [Stefan]
- patch 12 (now 11): don't initialize BlockAcctCookie to { 0 }, it's
not needed.
v2: https://lists.gnu.org/archive/html/qemu-block/2015-10/msg00161.html
- First complete implementation of the new statistics
v1: https://lists.gnu.org/archive/html/qemu-devel/2015-06/msg03321.html
- Initial series containing only the timed average infrastructure.
Alberto Garcia (21):
xen_disk: Account for flush operations
ide: Account for write operations correctly
block: define 'clock_type' for the accounting code
util: Infrastructure for computing recent averages
block: Add idle_time_ns to BlockDeviceStats
block: Add statistics for failed and invalid I/O operations
block: Allow configuring whether to account failed and invalid ops
block: Compute minimum, maximum and average I/O latencies
block: Add average I/O queue depth to BlockDeviceTimedStats
block: New option to define the intervals for collecting I/O
statistics
qemu-io: Account for failed, invalid and flush operations
block: Use QEMU_CLOCK_VIRTUAL for the accounting code in qtest mode
iotests: Add test for the block device statistics
nvme: Account for failed and invalid operations
virtio-blk: Account for failed and invalid operations
xen_disk: Account for failed and invalid operations
atapi: Account for failed and invalid operations
ide: Account for failed and invalid operations
macio: Account for failed operations
scsi-disk: Account for failed operations
block: Update copyright of the accounting code
block/accounting.c | 123 ++++++++++++++-
block/block-backend.c | 1 +
block/qapi.c | 51 +++++++
blockdev.c | 53 +++++++
hmp.c | 4 +-
hw/block/nvme.c | 11 +-
hw/block/virtio-blk.c | 4 +-
hw/block/xen_disk.c | 27 +++-
hw/ide/atapi.c | 31 ++--
hw/ide/core.c | 12 +-
hw/ide/macio.c | 12 +-
hw/scsi/scsi-disk.c | 46 ++++--
include/block/accounting.h | 28 ++++
include/qemu/timed-average.h | 64 ++++++++
qapi/block-core.json | 103 ++++++++++++-
qemu-io-cmds.c | 9 ++
qmp-commands.hx | 80 +++++++++-
tests/Makefile | 4 +
tests/qemu-iotests/136 | 349 +++++++++++++++++++++++++++++++++++++++++++
tests/qemu-iotests/136.out | 5 +
tests/qemu-iotests/group | 1 +
tests/test-timed-average.c | 90 +++++++++++
util/Makefile.objs | 1 +
util/timed-average.c | 231 ++++++++++++++++++++++++++++
24 files changed, 1292 insertions(+), 48 deletions(-)
create mode 100644 include/qemu/timed-average.h
create mode 100644 tests/qemu-iotests/136
create mode 100644 tests/qemu-iotests/136.out
create mode 100644 tests/test-timed-average.c
create mode 100644 util/timed-average.c
--
2.6.1
^ permalink raw reply [flat|nested] 24+ messages in thread
* [Qemu-devel] [PATCH v4 01/21] xen_disk: Account for flush operations
2015-10-28 15:32 [Qemu-devel] [PATCH v4 00/21] Extended I/O accounting Alberto Garcia
@ 2015-10-28 15:32 ` Alberto Garcia
2015-10-28 15:32 ` [Qemu-devel] [PATCH v4 02/21] ide: Account for write operations correctly Alberto Garcia
` (20 subsequent siblings)
21 siblings, 0 replies; 24+ messages in thread
From: Alberto Garcia @ 2015-10-28 15:32 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Alberto Garcia, qemu-block, Markus Armbruster,
Max Reitz, Stefan Hajnoczi
Currently both BLKIF_OP_WRITE and BLKIF_OP_FLUSH_DISKCACHE are being
accounted as write operations.
Signed-off-by: Alberto Garcia <berto@igalia.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
---
hw/block/xen_disk.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/hw/block/xen_disk.c b/hw/block/xen_disk.c
index 1bbc111..4869518 100644
--- a/hw/block/xen_disk.c
+++ b/hw/block/xen_disk.c
@@ -576,7 +576,9 @@ static int ioreq_runio_qemu_aio(struct ioreq *ioreq)
}
block_acct_start(blk_get_stats(blkdev->blk), &ioreq->acct,
- ioreq->v.size, BLOCK_ACCT_WRITE);
+ ioreq->v.size,
+ ioreq->req.operation == BLKIF_OP_WRITE ?
+ BLOCK_ACCT_WRITE : BLOCK_ACCT_FLUSH);
ioreq->aio_inflight++;
blk_aio_writev(blkdev->blk, ioreq->start / BLOCK_SIZE,
&ioreq->v, ioreq->v.size / BLOCK_SIZE,
--
2.6.1
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [Qemu-devel] [PATCH v4 02/21] ide: Account for write operations correctly
2015-10-28 15:32 [Qemu-devel] [PATCH v4 00/21] Extended I/O accounting Alberto Garcia
2015-10-28 15:32 ` [Qemu-devel] [PATCH v4 01/21] xen_disk: Account for flush operations Alberto Garcia
@ 2015-10-28 15:32 ` Alberto Garcia
2015-10-28 15:33 ` [Qemu-devel] [PATCH v4 03/21] block: define 'clock_type' for the accounting code Alberto Garcia
` (19 subsequent siblings)
21 siblings, 0 replies; 24+ messages in thread
From: Alberto Garcia @ 2015-10-28 15:32 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Alberto Garcia, qemu-block, Markus Armbruster,
Max Reitz, Stefan Hajnoczi
Signed-off-by: Alberto Garcia <berto@igalia.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
---
hw/ide/core.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/hw/ide/core.c b/hw/ide/core.c
index 317406d..b559f1b 100644
--- a/hw/ide/core.c
+++ b/hw/ide/core.c
@@ -895,7 +895,7 @@ static void ide_sector_write(IDEState *s)
qemu_iovec_init_external(&s->qiov, &s->iov, 1);
block_acct_start(blk_get_stats(s->blk), &s->acct,
- n * BDRV_SECTOR_SIZE, BLOCK_ACCT_READ);
+ n * BDRV_SECTOR_SIZE, BLOCK_ACCT_WRITE);
s->pio_aiocb = blk_aio_writev(s->blk, sector_num, &s->qiov, n,
ide_sector_write_cb, s);
}
--
2.6.1
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [Qemu-devel] [PATCH v4 03/21] block: define 'clock_type' for the accounting code
2015-10-28 15:32 [Qemu-devel] [PATCH v4 00/21] Extended I/O accounting Alberto Garcia
2015-10-28 15:32 ` [Qemu-devel] [PATCH v4 01/21] xen_disk: Account for flush operations Alberto Garcia
2015-10-28 15:32 ` [Qemu-devel] [PATCH v4 02/21] ide: Account for write operations correctly Alberto Garcia
@ 2015-10-28 15:33 ` Alberto Garcia
2015-10-28 15:33 ` [Qemu-devel] [PATCH v4 04/21] util: Infrastructure for computing recent averages Alberto Garcia
` (18 subsequent siblings)
21 siblings, 0 replies; 24+ messages in thread
From: Alberto Garcia @ 2015-10-28 15:33 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Alberto Garcia, qemu-block, Markus Armbruster,
Max Reitz, Stefan Hajnoczi
Its value is still QEMU_CLOCK_REALTIME, but having it in a variable will
allow us to change its value easily in the future when running in qtest
mode.
Signed-off-by: Alberto Garcia <berto@igalia.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
---
block/accounting.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/block/accounting.c b/block/accounting.c
index a423560..6f4c0f1 100644
--- a/block/accounting.c
+++ b/block/accounting.c
@@ -26,13 +26,15 @@
#include "block/block_int.h"
#include "qemu/timer.h"
+static QEMUClockType clock_type = QEMU_CLOCK_REALTIME;
+
void block_acct_start(BlockAcctStats *stats, BlockAcctCookie *cookie,
int64_t bytes, enum BlockAcctType type)
{
assert(type < BLOCK_MAX_IOTYPE);
cookie->bytes = bytes;
- cookie->start_time_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
+ cookie->start_time_ns = qemu_clock_get_ns(clock_type);
cookie->type = type;
}
@@ -43,7 +45,7 @@ void block_acct_done(BlockAcctStats *stats, BlockAcctCookie *cookie)
stats->nr_bytes[cookie->type] += cookie->bytes;
stats->nr_ops[cookie->type]++;
stats->total_time_ns[cookie->type] +=
- qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - cookie->start_time_ns;
+ qemu_clock_get_ns(clock_type) - cookie->start_time_ns;
}
--
2.6.1
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [Qemu-devel] [PATCH v4 04/21] util: Infrastructure for computing recent averages
2015-10-28 15:32 [Qemu-devel] [PATCH v4 00/21] Extended I/O accounting Alberto Garcia
` (2 preceding siblings ...)
2015-10-28 15:33 ` [Qemu-devel] [PATCH v4 03/21] block: define 'clock_type' for the accounting code Alberto Garcia
@ 2015-10-28 15:33 ` Alberto Garcia
2015-10-28 15:33 ` [Qemu-devel] [PATCH v4 05/21] block: Add idle_time_ns to BlockDeviceStats Alberto Garcia
` (17 subsequent siblings)
21 siblings, 0 replies; 24+ messages in thread
From: Alberto Garcia @ 2015-10-28 15:33 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Alberto Garcia, qemu-block, Markus Armbruster,
Max Reitz, Stefan Hajnoczi
This module computes the average of a set of values within a time
window, keeping also track of the minimum and maximum values.
In order to produce more accurate results it works internally by
creating two time windows of the same period, offsetted by half of
that period. Values are accounted on both windows and the data is
always returned from the oldest one.
Signed-off-by: Alberto Garcia <berto@igalia.com>
---
include/qemu/timed-average.h | 63 +++++++++++++
tests/Makefile | 4 +
tests/test-timed-average.c | 90 +++++++++++++++++++
util/Makefile.objs | 1 +
util/timed-average.c | 210 +++++++++++++++++++++++++++++++++++++++++++
5 files changed, 368 insertions(+)
create mode 100644 include/qemu/timed-average.h
create mode 100644 tests/test-timed-average.c
create mode 100644 util/timed-average.c
diff --git a/include/qemu/timed-average.h b/include/qemu/timed-average.h
new file mode 100644
index 0000000..f1cdddc
--- /dev/null
+++ b/include/qemu/timed-average.h
@@ -0,0 +1,63 @@
+/*
+ * QEMU timed average computation
+ *
+ * Copyright (C) Nodalink, EURL. 2014
+ * Copyright (C) Igalia, S.L. 2015
+ *
+ * Authors:
+ * Benoît Canet <benoit.canet@nodalink.com>
+ * Alberto Garcia <berto@igalia.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) version 3 or any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef TIMED_AVERAGE_H
+#define TIMED_AVERAGE_H
+
+#include <stdint.h>
+
+#include "qemu/timer.h"
+
+typedef struct TimedAverageWindow TimedAverageWindow;
+typedef struct TimedAverage TimedAverage;
+
+/* All fields of both structures are private */
+
+struct TimedAverageWindow {
+ uint64_t min; /* minimum value accounted in the window */
+ uint64_t max; /* maximum value accounted in the window */
+ uint64_t sum; /* sum of all values */
+ uint64_t count; /* number of values */
+ int64_t expiration; /* the end of the current window in ns */
+};
+
+struct TimedAverage {
+ uint64_t period; /* period in nanoseconds */
+ TimedAverageWindow windows[2]; /* two overlapping windows of with
+ * an offset of period / 2 between them */
+ unsigned current; /* the current window index: it's also the
+ * oldest window index */
+ QEMUClockType clock_type; /* the clock used */
+};
+
+void timed_average_init(TimedAverage *ta, QEMUClockType clock_type,
+ uint64_t period);
+
+void timed_average_account(TimedAverage *ta, uint64_t value);
+
+uint64_t timed_average_min(TimedAverage *ta);
+uint64_t timed_average_avg(TimedAverage *ta);
+uint64_t timed_average_max(TimedAverage *ta);
+
+#endif
diff --git a/tests/Makefile b/tests/Makefile
index 1c57e39..38acf02 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -81,6 +81,7 @@ check-unit-y += tests/test-crypto-cipher$(EXESUF)
check-unit-$(CONFIG_GNUTLS) += tests/test-crypto-tlscredsx509$(EXESUF)
check-unit-$(CONFIG_GNUTLS) += tests/test-crypto-tlssession$(EXESUF)
check-unit-$(CONFIG_LINUX) += tests/test-qga$(EXESUF)
+check-unit-y += tests/test-timed-average$(EXESUF)
check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh
@@ -405,6 +406,9 @@ tests/test-vmstate$(EXESUF): tests/test-vmstate.o \
migration/vmstate.o migration/qemu-file.o migration/qemu-file-buf.o \
migration/qemu-file-unix.o qjson.o \
$(test-qom-obj-y)
+tests/test-timed-average$(EXESUF): tests/test-timed-average.o qemu-timer.o \
+ libqemuutil.a stubs/clock-warp.o stubs/cpu-get-icount.o \
+ stubs/notify-event.o
tests/test-qapi-types.c tests/test-qapi-types.h :\
$(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-types.py $(qapi-py)
diff --git a/tests/test-timed-average.c b/tests/test-timed-average.c
new file mode 100644
index 0000000..a049799
--- /dev/null
+++ b/tests/test-timed-average.c
@@ -0,0 +1,90 @@
+/*
+ * Timed average computation tests
+ *
+ * Copyright Nodalink, EURL. 2014
+ *
+ * Authors:
+ * Benoît Canet <benoit.canet@nodalink.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+
+#include <glib.h>
+#include <unistd.h>
+
+#include "qemu/timed-average.h"
+
+/* This is the clock for QEMU_CLOCK_VIRTUAL */
+static int64_t my_clock_value;
+
+int64_t cpu_get_clock(void)
+{
+ return my_clock_value;
+}
+
+static void account(TimedAverage *ta)
+{
+ timed_average_account(ta, 1);
+ timed_average_account(ta, 5);
+ timed_average_account(ta, 2);
+ timed_average_account(ta, 4);
+ timed_average_account(ta, 3);
+}
+
+static void test_average(void)
+{
+ TimedAverage ta;
+ uint64_t result;
+ int i;
+
+ /* we will compute some average on a period of 1 second */
+ timed_average_init(&ta, QEMU_CLOCK_VIRTUAL, NANOSECONDS_PER_SECOND);
+
+ result = timed_average_min(&ta);
+ g_assert(result == 0);
+ result = timed_average_avg(&ta);
+ g_assert(result == 0);
+ result = timed_average_max(&ta);
+ g_assert(result == 0);
+
+ for (i = 0; i < 100; i++) {
+ account(&ta);
+ result = timed_average_min(&ta);
+ g_assert(result == 1);
+ result = timed_average_avg(&ta);
+ g_assert(result == 3);
+ result = timed_average_max(&ta);
+ g_assert(result == 5);
+ my_clock_value += NANOSECONDS_PER_SECOND / 10;
+ }
+
+ my_clock_value += NANOSECONDS_PER_SECOND * 100;
+
+ result = timed_average_min(&ta);
+ g_assert(result == 0);
+ result = timed_average_avg(&ta);
+ g_assert(result == 0);
+ result = timed_average_max(&ta);
+ g_assert(result == 0);
+
+ for (i = 0; i < 100; i++) {
+ account(&ta);
+ result = timed_average_min(&ta);
+ g_assert(result == 1);
+ result = timed_average_avg(&ta);
+ g_assert(result == 3);
+ result = timed_average_max(&ta);
+ g_assert(result == 5);
+ my_clock_value += NANOSECONDS_PER_SECOND / 10;
+ }
+}
+
+int main(int argc, char **argv)
+{
+ /* tests in the same order as the header function declarations */
+ g_test_init(&argc, &argv, NULL);
+ g_test_add_func("/timed-average/average", test_average);
+ return g_test_run();
+}
+
diff --git a/util/Makefile.objs b/util/Makefile.objs
index d7cc399..89dd80e 100644
--- a/util/Makefile.objs
+++ b/util/Makefile.objs
@@ -29,3 +29,4 @@ util-obj-y += qemu-coroutine.o qemu-coroutine-lock.o qemu-coroutine-io.o
util-obj-y += qemu-coroutine-sleep.o
util-obj-y += coroutine-$(CONFIG_COROUTINE_BACKEND).o
util-obj-y += buffer.o
+util-obj-y += timed-average.o
diff --git a/util/timed-average.c b/util/timed-average.c
new file mode 100644
index 0000000..98a1170
--- /dev/null
+++ b/util/timed-average.c
@@ -0,0 +1,210 @@
+/*
+ * QEMU timed average computation
+ *
+ * Copyright (C) Nodalink, EURL. 2014
+ * Copyright (C) Igalia, S.L. 2015
+ *
+ * Authors:
+ * Benoît Canet <benoit.canet@nodalink.com>
+ * Alberto Garcia <berto@igalia.com>
+ *
+ * This program is free sofware: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Sofware Foundation, either version 2 of the License, or
+ * (at your option) version 3 or any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <string.h>
+
+#include "qemu/timed-average.h"
+
+/* This module computes an average of a set of values within a time
+ * window.
+ *
+ * Algorithm:
+ *
+ * - Create two windows with a certain expiration period, and
+ * offsetted by period / 2.
+ * - Each time you want to account a new value, do it in both windows.
+ * - The minimum / maximum / average values are always returned from
+ * the oldest window.
+ *
+ * Example:
+ *
+ * t=0 |t=0.5 |t=1 |t=1.5 |t=2
+ * wnd0: [0,0.5)|wnd0: [0.5,1.5) | |wnd0: [1.5,2.5) |
+ * wnd1: [0,1) | |wnd1: [1,2) | |
+ *
+ * Values are returned from:
+ *
+ * wnd0---------|wnd1------------|wnd0---------|wnd1-------------|
+ */
+
+/* Update the expiration of a time window
+ *
+ * @w: the window used
+ * @now: the current time in nanoseconds
+ * @period: the expiration period in nanoseconds
+ */
+static void update_expiration(TimedAverageWindow *w, int64_t now,
+ int64_t period)
+{
+ /* time elapsed since the last theoretical expiration */
+ int64_t elapsed = (now - w->expiration) % period;
+ /* time remaininging until the next expiration */
+ int64_t remaining = period - elapsed;
+ /* compute expiration */
+ w->expiration = now + remaining;
+}
+
+/* Reset a window
+ *
+ * @w: the window to reset
+ */
+static void window_reset(TimedAverageWindow *w)
+{
+ w->min = UINT64_MAX;
+ w->max = 0;
+ w->sum = 0;
+ w->count = 0;
+}
+
+/* Get the current window (that is, the one with the earliest
+ * expiration time).
+ *
+ * @ta: the TimedAverage structure
+ * @ret: a pointer to the current window
+ */
+static TimedAverageWindow *current_window(TimedAverage *ta)
+{
+ return &ta->windows[ta->current];
+}
+
+/* Initialize a TimedAverage structure
+ *
+ * @ta: the TimedAverage structure
+ * @clock_type: the type of clock to use
+ * @period: the time window period in nanoseconds
+ */
+void timed_average_init(TimedAverage *ta, QEMUClockType clock_type,
+ uint64_t period)
+{
+ int64_t now = qemu_clock_get_ns(clock_type);
+
+ /* Returned values are from the oldest window, so they belong to
+ * the interval [ta->period/2,ta->period). By adjusting the
+ * requested period by 4/3, we guarantee that they're in the
+ * interval [2/3 period,4/3 period), closer to the requested
+ * period on average */
+ ta->period = (uint64_t) period * 4 / 3;
+ ta->clock_type = clock_type;
+ ta->current = 0;
+
+ window_reset(&ta->windows[0]);
+ window_reset(&ta->windows[1]);
+
+ /* Both windows are offsetted by half a period */
+ ta->windows[0].expiration = now + ta->period / 2;
+ ta->windows[1].expiration = now + ta->period;
+}
+
+/* Check if the time windows have expired, updating their counters and
+ * expiration time if that's the case.
+ *
+ * @ta: the TimedAverage structure
+ */
+static void check_expirations(TimedAverage *ta)
+{
+ int64_t now = qemu_clock_get_ns(ta->clock_type);
+ int i;
+
+ assert(ta->period != 0);
+
+ /* Check if the windows have expired */
+ for (i = 0; i < 2; i++) {
+ TimedAverageWindow *w = &ta->windows[i];
+ if (w->expiration <= now) {
+ window_reset(w);
+ update_expiration(w, now, ta->period);
+ }
+ }
+
+ /* Make ta->current point to the oldest window */
+ if (ta->windows[0].expiration < ta->windows[1].expiration) {
+ ta->current = 0;
+ } else {
+ ta->current = 1;
+ }
+}
+
+/* Account a value
+ *
+ * @ta: the TimedAverage structure
+ * @value: the value to account
+ */
+void timed_average_account(TimedAverage *ta, uint64_t value)
+{
+ int i;
+ check_expirations(ta);
+
+ /* Do the accounting in both windows at the same time */
+ for (i = 0; i < 2; i++) {
+ TimedAverageWindow *w = &ta->windows[i];
+
+ w->sum += value;
+ w->count++;
+
+ if (value < w->min) {
+ w->min = value;
+ }
+
+ if (value > w->max) {
+ w->max = value;
+ }
+ }
+}
+
+/* Get the minimum value
+ *
+ * @ta: the TimedAverage structure
+ * @ret: the minimum value
+ */
+uint64_t timed_average_min(TimedAverage *ta)
+{
+ TimedAverageWindow *w;
+ check_expirations(ta);
+ w = current_window(ta);
+ return w->min < UINT64_MAX ? w->min : 0;
+}
+
+/* Get the average value
+ *
+ * @ta: the TimedAverage structure
+ * @ret: the average value
+ */
+uint64_t timed_average_avg(TimedAverage *ta)
+{
+ TimedAverageWindow *w;
+ check_expirations(ta);
+ w = current_window(ta);
+ return w->count > 0 ? w->sum / w->count : 0;
+}
+
+/* Get the maximum value
+ *
+ * @ta: the TimedAverage structure
+ * @ret: the maximum value
+ */
+uint64_t timed_average_max(TimedAverage *ta)
+{
+ check_expirations(ta);
+ return current_window(ta)->max;
+}
--
2.6.1
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [Qemu-devel] [PATCH v4 05/21] block: Add idle_time_ns to BlockDeviceStats
2015-10-28 15:32 [Qemu-devel] [PATCH v4 00/21] Extended I/O accounting Alberto Garcia
` (3 preceding siblings ...)
2015-10-28 15:33 ` [Qemu-devel] [PATCH v4 04/21] util: Infrastructure for computing recent averages Alberto Garcia
@ 2015-10-28 15:33 ` Alberto Garcia
2015-10-28 15:33 ` [Qemu-devel] [PATCH v4 06/21] block: Add statistics for failed and invalid I/O operations Alberto Garcia
` (16 subsequent siblings)
21 siblings, 0 replies; 24+ messages in thread
From: Alberto Garcia @ 2015-10-28 15:33 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Alberto Garcia, qemu-block, Markus Armbruster,
Max Reitz, Stefan Hajnoczi
This patch adds the new field 'idle_time_ns' to the BlockDeviceStats
structure, indicating the time that has passed since the previous I/O
operation.
It also adds the block_acct_idle_time_ns() call, to ensure that all
references to the clock type used for accounting are in the same
place. This will later allow us to use a different clock for iotests.
Signed-off-by: Alberto Garcia <berto@igalia.com>
---
block/accounting.c | 12 ++++++++++--
block/qapi.c | 5 +++++
hmp.c | 4 +++-
include/block/accounting.h | 2 ++
qapi/block-core.json | 6 +++++-
qmp-commands.hx | 10 ++++++++--
6 files changed, 33 insertions(+), 6 deletions(-)
diff --git a/block/accounting.c b/block/accounting.c
index 6f4c0f1..d427fa8 100644
--- a/block/accounting.c
+++ b/block/accounting.c
@@ -40,12 +40,15 @@ void block_acct_start(BlockAcctStats *stats, BlockAcctCookie *cookie,
void block_acct_done(BlockAcctStats *stats, BlockAcctCookie *cookie)
{
+ int64_t time_ns = qemu_clock_get_ns(clock_type);
+ int64_t latency_ns = time_ns - cookie->start_time_ns;
+
assert(cookie->type < BLOCK_MAX_IOTYPE);
stats->nr_bytes[cookie->type] += cookie->bytes;
stats->nr_ops[cookie->type]++;
- stats->total_time_ns[cookie->type] +=
- qemu_clock_get_ns(clock_type) - cookie->start_time_ns;
+ stats->total_time_ns[cookie->type] += latency_ns;
+ stats->last_access_time_ns = time_ns;
}
@@ -55,3 +58,8 @@ void block_acct_merge_done(BlockAcctStats *stats, enum BlockAcctType type,
assert(type < BLOCK_MAX_IOTYPE);
stats->merged[type] += num_requests;
}
+
+int64_t block_acct_idle_time_ns(BlockAcctStats *stats)
+{
+ return qemu_clock_get_ns(clock_type) - stats->last_access_time_ns;
+}
diff --git a/block/qapi.c b/block/qapi.c
index ec0f513..539c2e3 100644
--- a/block/qapi.c
+++ b/block/qapi.c
@@ -357,6 +357,11 @@ static BlockStats *bdrv_query_stats(const BlockDriverState *bs,
s->stats->wr_total_time_ns = stats->total_time_ns[BLOCK_ACCT_WRITE];
s->stats->rd_total_time_ns = stats->total_time_ns[BLOCK_ACCT_READ];
s->stats->flush_total_time_ns = stats->total_time_ns[BLOCK_ACCT_FLUSH];
+
+ s->stats->has_idle_time_ns = stats->last_access_time_ns > 0;
+ if (s->stats->has_idle_time_ns) {
+ s->stats->idle_time_ns = block_acct_idle_time_ns(stats);
+ }
}
s->stats->wr_highest_offset = bs->wr_highest_offset;
diff --git a/hmp.c b/hmp.c
index 5048eee..1c33440 100644
--- a/hmp.c
+++ b/hmp.c
@@ -521,6 +521,7 @@ void hmp_info_blockstats(Monitor *mon, const QDict *qdict)
" flush_total_time_ns=%" PRId64
" rd_merged=%" PRId64
" wr_merged=%" PRId64
+ " idle_time_ns=%" PRId64
"\n",
stats->value->stats->rd_bytes,
stats->value->stats->wr_bytes,
@@ -531,7 +532,8 @@ void hmp_info_blockstats(Monitor *mon, const QDict *qdict)
stats->value->stats->rd_total_time_ns,
stats->value->stats->flush_total_time_ns,
stats->value->stats->rd_merged,
- stats->value->stats->wr_merged);
+ stats->value->stats->wr_merged,
+ stats->value->stats->idle_time_ns);
}
qapi_free_BlockStatsList(stats_list);
diff --git a/include/block/accounting.h b/include/block/accounting.h
index 66637cd..4b2b999 100644
--- a/include/block/accounting.h
+++ b/include/block/accounting.h
@@ -40,6 +40,7 @@ typedef struct BlockAcctStats {
uint64_t nr_ops[BLOCK_MAX_IOTYPE];
uint64_t total_time_ns[BLOCK_MAX_IOTYPE];
uint64_t merged[BLOCK_MAX_IOTYPE];
+ int64_t last_access_time_ns;
} BlockAcctStats;
typedef struct BlockAcctCookie {
@@ -53,5 +54,6 @@ void block_acct_start(BlockAcctStats *stats, BlockAcctCookie *cookie,
void block_acct_done(BlockAcctStats *stats, BlockAcctCookie *cookie);
void block_acct_merge_done(BlockAcctStats *stats, enum BlockAcctType type,
int num_requests);
+int64_t block_acct_idle_time_ns(BlockAcctStats *stats);
#endif
diff --git a/qapi/block-core.json b/qapi/block-core.json
index 425fdab..b00be46 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -448,6 +448,10 @@
# @wr_merged: Number of write requests that have been merged into another
# request (Since 2.3).
#
+# @idle_time_ns: #optional Time since the last I/O operation, in
+# nanoseconds. If the field is absent it means that
+# there haven't been any operations yet (Since 2.5).
+#
# Since: 0.14.0
##
{ 'struct': 'BlockDeviceStats',
@@ -455,7 +459,7 @@
'wr_operations': 'int', 'flush_operations': 'int',
'flush_total_time_ns': 'int', 'wr_total_time_ns': 'int',
'rd_total_time_ns': 'int', 'wr_highest_offset': 'int',
- 'rd_merged': 'int', 'wr_merged': 'int' } }
+ 'rd_merged': 'int', 'wr_merged': 'int', '*idle_time_ns': 'int' } }
##
# @BlockStats:
diff --git a/qmp-commands.hx b/qmp-commands.hx
index d7cf0ff..e90d2c1 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -2526,6 +2526,10 @@ Each json-object contain the following:
another request (json-int)
- "wr_merged": number of write requests that have been merged into
another request (json-int)
+ - "idle_time_ns": time since the last I/O operation, in
+ nanoseconds. If the field is absent it means
+ that there haven't been any operations yet
+ (json-int, optional)
- "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
@@ -2550,7 +2554,8 @@ Example:
"flush_total_times_ns":49653
"flush_operations":61,
"rd_merged":0,
- "wr_merged":0
+ "wr_merged":0,
+ "idle_time_ns":2953431879
}
},
"stats":{
@@ -2564,7 +2569,8 @@ Example:
"rd_total_times_ns":3465673657
"flush_total_times_ns":49653,
"rd_merged":0,
- "wr_merged":0
+ "wr_merged":0,
+ "idle_time_ns":2953431879
}
},
{
--
2.6.1
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [Qemu-devel] [PATCH v4 06/21] block: Add statistics for failed and invalid I/O operations
2015-10-28 15:32 [Qemu-devel] [PATCH v4 00/21] Extended I/O accounting Alberto Garcia
` (4 preceding siblings ...)
2015-10-28 15:33 ` [Qemu-devel] [PATCH v4 05/21] block: Add idle_time_ns to BlockDeviceStats Alberto Garcia
@ 2015-10-28 15:33 ` Alberto Garcia
2015-10-28 15:33 ` [Qemu-devel] [PATCH v4 07/21] block: Allow configuring whether to account failed and invalid ops Alberto Garcia
` (15 subsequent siblings)
21 siblings, 0 replies; 24+ messages in thread
From: Alberto Garcia @ 2015-10-28 15:33 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Alberto Garcia, qemu-block, Markus Armbruster,
Max Reitz, Stefan Hajnoczi
This patch adds the block_acct_failed() and block_acct_invalid()
functions to allow keeping track of failed and invalid I/O operations.
The number of failed and invalid operations is exposed in
BlockDeviceStats.
We don't keep track of the time spent on invalid operations because
they are cancelled immediately when they are started.
Signed-off-by: Alberto Garcia <berto@igalia.com>
---
block/accounting.c | 23 +++++++++++++++++++++++
block/qapi.c | 10 ++++++++++
include/block/accounting.h | 4 ++++
qapi/block-core.json | 23 ++++++++++++++++++++++-
qmp-commands.hx | 12 ++++++++++++
5 files changed, 71 insertions(+), 1 deletion(-)
diff --git a/block/accounting.c b/block/accounting.c
index d427fa8..49a9444 100644
--- a/block/accounting.c
+++ b/block/accounting.c
@@ -51,6 +51,29 @@ void block_acct_done(BlockAcctStats *stats, BlockAcctCookie *cookie)
stats->last_access_time_ns = time_ns;
}
+void block_acct_failed(BlockAcctStats *stats, BlockAcctCookie *cookie)
+{
+ int64_t time_ns = qemu_clock_get_ns(clock_type);
+
+ assert(cookie->type < BLOCK_MAX_IOTYPE);
+
+ stats->failed_ops[cookie->type]++;
+ stats->total_time_ns[cookie->type] += time_ns - cookie->start_time_ns;
+ stats->last_access_time_ns = time_ns;
+}
+
+void block_acct_invalid(BlockAcctStats *stats, enum BlockAcctType type)
+{
+ assert(type < BLOCK_MAX_IOTYPE);
+
+ /* block_acct_done() and block_acct_failed() update
+ * total_time_ns[], but this one does not. The reason is that
+ * invalid requests are accounted during their submission,
+ * therefore there's no actual I/O involved. */
+
+ stats->invalid_ops[type]++;
+ stats->last_access_time_ns = qemu_clock_get_ns(clock_type);
+}
void block_acct_merge_done(BlockAcctStats *stats, enum BlockAcctType type,
int num_requests)
diff --git a/block/qapi.c b/block/qapi.c
index 539c2e3..84d8412 100644
--- a/block/qapi.c
+++ b/block/qapi.c
@@ -351,6 +351,16 @@ static BlockStats *bdrv_query_stats(const BlockDriverState *bs,
s->stats->wr_bytes = stats->nr_bytes[BLOCK_ACCT_WRITE];
s->stats->rd_operations = stats->nr_ops[BLOCK_ACCT_READ];
s->stats->wr_operations = stats->nr_ops[BLOCK_ACCT_WRITE];
+
+ s->stats->failed_rd_operations = stats->failed_ops[BLOCK_ACCT_READ];
+ s->stats->failed_wr_operations = stats->failed_ops[BLOCK_ACCT_WRITE];
+ s->stats->failed_flush_operations = stats->failed_ops[BLOCK_ACCT_FLUSH];
+
+ s->stats->invalid_rd_operations = stats->invalid_ops[BLOCK_ACCT_READ];
+ s->stats->invalid_wr_operations = stats->invalid_ops[BLOCK_ACCT_WRITE];
+ s->stats->invalid_flush_operations =
+ stats->invalid_ops[BLOCK_ACCT_FLUSH];
+
s->stats->rd_merged = stats->merged[BLOCK_ACCT_READ];
s->stats->wr_merged = stats->merged[BLOCK_ACCT_WRITE];
s->stats->flush_operations = stats->nr_ops[BLOCK_ACCT_FLUSH];
diff --git a/include/block/accounting.h b/include/block/accounting.h
index 4b2b999..b50e3cc 100644
--- a/include/block/accounting.h
+++ b/include/block/accounting.h
@@ -38,6 +38,8 @@ enum BlockAcctType {
typedef struct BlockAcctStats {
uint64_t nr_bytes[BLOCK_MAX_IOTYPE];
uint64_t nr_ops[BLOCK_MAX_IOTYPE];
+ uint64_t invalid_ops[BLOCK_MAX_IOTYPE];
+ uint64_t failed_ops[BLOCK_MAX_IOTYPE];
uint64_t total_time_ns[BLOCK_MAX_IOTYPE];
uint64_t merged[BLOCK_MAX_IOTYPE];
int64_t last_access_time_ns;
@@ -52,6 +54,8 @@ typedef struct BlockAcctCookie {
void block_acct_start(BlockAcctStats *stats, BlockAcctCookie *cookie,
int64_t bytes, enum BlockAcctType type);
void block_acct_done(BlockAcctStats *stats, BlockAcctCookie *cookie);
+void block_acct_failed(BlockAcctStats *stats, BlockAcctCookie *cookie);
+void block_acct_invalid(BlockAcctStats *stats, enum BlockAcctType type);
void block_acct_merge_done(BlockAcctStats *stats, enum BlockAcctType type,
int num_requests);
int64_t block_acct_idle_time_ns(BlockAcctStats *stats);
diff --git a/qapi/block-core.json b/qapi/block-core.json
index b00be46..0718243 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -452,6 +452,24 @@
# nanoseconds. If the field is absent it means that
# there haven't been any operations yet (Since 2.5).
#
+# @failed_rd_operations: The number of failed read operations
+# performed by the device (Since 2.5)
+#
+# @failed_wr_operations: The number of failed write operations
+# performed by the device (Since 2.5)
+#
+# @failed_flush_operations: The number of failed flush operations
+# performed by the device (Since 2.5)
+#
+# @invalid_rd_operations: The number of invalid read operations
+# performed by the device (Since 2.5)
+#
+# @invalid_wr_operations: The number of invalid write operations
+# performed by the device (Since 2.5)
+#
+# @invalid_flush_operations: The number of invalid flush operations
+# performed by the device (Since 2.5)
+#
# Since: 0.14.0
##
{ 'struct': 'BlockDeviceStats',
@@ -459,7 +477,10 @@
'wr_operations': 'int', 'flush_operations': 'int',
'flush_total_time_ns': 'int', 'wr_total_time_ns': 'int',
'rd_total_time_ns': 'int', 'wr_highest_offset': 'int',
- 'rd_merged': 'int', 'wr_merged': 'int', '*idle_time_ns': 'int' } }
+ 'rd_merged': 'int', 'wr_merged': 'int', '*idle_time_ns': 'int',
+ 'failed_rd_operations': 'int', 'failed_wr_operations': 'int',
+ 'failed_flush_operations': 'int', 'invalid_rd_operations': 'int',
+ 'invalid_wr_operations': 'int', 'invalid_flush_operations': 'int' } }
##
# @BlockStats:
diff --git a/qmp-commands.hx b/qmp-commands.hx
index e90d2c1..65936bd 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -2530,6 +2530,18 @@ Each json-object contain the following:
nanoseconds. If the field is absent it means
that there haven't been any operations yet
(json-int, optional)
+ - "failed_rd_operations": number of failed read operations
+ (json-int)
+ - "failed_wr_operations": number of failed write operations
+ (json-int)
+ - "failed_flush_operations": number of failed flush operations
+ (json-int)
+ - "invalid_rd_operations": number of invalid read operations
+ (json-int)
+ - "invalid_wr_operations": number of invalid write operations
+ (json-int)
+ - "invalid_flush_operations": number of invalid flush operations
+ (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.6.1
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [Qemu-devel] [PATCH v4 07/21] block: Allow configuring whether to account failed and invalid ops
2015-10-28 15:32 [Qemu-devel] [PATCH v4 00/21] Extended I/O accounting Alberto Garcia
` (5 preceding siblings ...)
2015-10-28 15:33 ` [Qemu-devel] [PATCH v4 06/21] block: Add statistics for failed and invalid I/O operations Alberto Garcia
@ 2015-10-28 15:33 ` Alberto Garcia
2015-10-28 15:33 ` [Qemu-devel] [PATCH v4 08/21] block: Compute minimum, maximum and average I/O latencies Alberto Garcia
` (14 subsequent siblings)
21 siblings, 0 replies; 24+ messages in thread
From: Alberto Garcia @ 2015-10-28 15:33 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Alberto Garcia, qemu-block, Markus Armbruster,
Max Reitz, Stefan Hajnoczi
This patch adds two options, "stats-account-invalid" and
"stats-account-failed", that can be used to decide whether invalid and
failed I/O operations must be used when collecting statistics for
latency and last access time.
Signed-off-by: Alberto Garcia <berto@igalia.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
---
block/accounting.c | 24 +++++++++++++++++++-----
block/qapi.c | 3 +++
blockdev.c | 16 ++++++++++++++++
include/block/accounting.h | 5 +++++
qapi/block-core.json | 17 ++++++++++++++++-
qmp-commands.hx | 25 ++++++++++++++++++++-----
6 files changed, 79 insertions(+), 11 deletions(-)
diff --git a/block/accounting.c b/block/accounting.c
index 49a9444..923aeaf 100644
--- a/block/accounting.c
+++ b/block/accounting.c
@@ -28,6 +28,13 @@
static QEMUClockType clock_type = QEMU_CLOCK_REALTIME;
+void block_acct_init(BlockAcctStats *stats, bool account_invalid,
+ bool account_failed)
+{
+ stats->account_invalid = account_invalid;
+ stats->account_failed = account_failed;
+}
+
void block_acct_start(BlockAcctStats *stats, BlockAcctCookie *cookie,
int64_t bytes, enum BlockAcctType type)
{
@@ -53,13 +60,17 @@ void block_acct_done(BlockAcctStats *stats, BlockAcctCookie *cookie)
void block_acct_failed(BlockAcctStats *stats, BlockAcctCookie *cookie)
{
- int64_t time_ns = qemu_clock_get_ns(clock_type);
-
assert(cookie->type < BLOCK_MAX_IOTYPE);
stats->failed_ops[cookie->type]++;
- stats->total_time_ns[cookie->type] += time_ns - cookie->start_time_ns;
- stats->last_access_time_ns = time_ns;
+
+ if (stats->account_failed) {
+ 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;
+ }
}
void block_acct_invalid(BlockAcctStats *stats, enum BlockAcctType type)
@@ -72,7 +83,10 @@ void block_acct_invalid(BlockAcctStats *stats, enum BlockAcctType type)
* therefore there's no actual I/O involved. */
stats->invalid_ops[type]++;
- stats->last_access_time_ns = qemu_clock_get_ns(clock_type);
+
+ if (stats->account_invalid) {
+ stats->last_access_time_ns = qemu_clock_get_ns(clock_type);
+ }
}
void block_acct_merge_done(BlockAcctStats *stats, enum BlockAcctType type,
diff --git a/block/qapi.c b/block/qapi.c
index 84d8412..56c8139 100644
--- a/block/qapi.c
+++ b/block/qapi.c
@@ -372,6 +372,9 @@ static BlockStats *bdrv_query_stats(const BlockDriverState *bs,
if (s->stats->has_idle_time_ns) {
s->stats->idle_time_ns = block_acct_idle_time_ns(stats);
}
+
+ s->stats->account_invalid = stats->account_invalid;
+ s->stats->account_failed = stats->account_failed;
}
s->stats->wr_highest_offset = bs->wr_highest_offset;
diff --git a/blockdev.c b/blockdev.c
index 18712d2..82cd6a0 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -467,6 +467,7 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
const char *buf;
int bdrv_flags = 0;
int on_read_error, on_write_error;
+ bool account_invalid, account_failed;
BlockBackend *blk;
BlockDriverState *bs;
ThrottleConfig cfg;
@@ -503,6 +504,9 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
/* extract parameters */
snapshot = qemu_opt_get_bool(opts, "snapshot", 0);
+ account_invalid = qemu_opt_get_bool(opts, "stats-account-invalid", true);
+ account_failed = qemu_opt_get_bool(opts, "stats-account-failed", true);
+
extract_common_blockdev_options(opts, &bdrv_flags, &throttling_group, &cfg,
&detect_zeroes, &error);
if (error) {
@@ -599,6 +603,8 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
if (bdrv_key_required(bs)) {
autostart = 0;
}
+
+ block_acct_init(blk_get_stats(blk), account_invalid, account_failed);
}
blk_set_on_error(blk, on_read_error, on_write_error);
@@ -3404,6 +3410,16 @@ QemuOptsList qemu_common_drive_opts = {
.name = "detect-zeroes",
.type = QEMU_OPT_STRING,
.help = "try to optimize zero writes (off, on, unmap)",
+ },{
+ .name = "stats-account-invalid",
+ .type = QEMU_OPT_BOOL,
+ .help = "whether to account for invalid I/O operations "
+ "in the statistics",
+ },{
+ .name = "stats-account-failed",
+ .type = QEMU_OPT_BOOL,
+ .help = "whether to account for failed I/O operations "
+ "in the statistics",
},
{ /* end of list */ }
},
diff --git a/include/block/accounting.h b/include/block/accounting.h
index b50e3cc..0d9b076 100644
--- a/include/block/accounting.h
+++ b/include/block/accounting.h
@@ -25,6 +25,7 @@
#define BLOCK_ACCOUNTING_H
#include <stdint.h>
+#include <stdbool.h>
#include "qemu/typedefs.h"
@@ -43,6 +44,8 @@ typedef struct BlockAcctStats {
uint64_t total_time_ns[BLOCK_MAX_IOTYPE];
uint64_t merged[BLOCK_MAX_IOTYPE];
int64_t last_access_time_ns;
+ bool account_invalid;
+ bool account_failed;
} BlockAcctStats;
typedef struct BlockAcctCookie {
@@ -51,6 +54,8 @@ typedef struct BlockAcctCookie {
enum BlockAcctType type;
} BlockAcctCookie;
+void block_acct_init(BlockAcctStats *stats, bool account_invalid,
+ bool account_failed);
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 0718243..b33663b 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -470,6 +470,12 @@
# @invalid_flush_operations: The number of invalid flush operations
# performed by the device (Since 2.5)
#
+# @account_invalid: Whether invalid operations are included in the
+# last access statistics (Since 2.5)
+#
+# @account_failed: Whether failed operations are included in the
+# latency and last access statistics (Since 2.5)
+#
# Since: 0.14.0
##
{ 'struct': 'BlockDeviceStats',
@@ -480,7 +486,8 @@
'rd_merged': 'int', 'wr_merged': 'int', '*idle_time_ns': 'int',
'failed_rd_operations': 'int', 'failed_wr_operations': 'int',
'failed_flush_operations': 'int', 'invalid_rd_operations': 'int',
- 'invalid_wr_operations': 'int', 'invalid_flush_operations': 'int' } }
+ 'invalid_wr_operations': 'int', 'invalid_flush_operations': 'int',
+ 'account_invalid': 'bool', 'account_failed': 'bool' } }
##
# @BlockStats:
@@ -1433,6 +1440,12 @@
# (default: enospc)
# @read-only: #optional whether the block device should be read-only
# (default: false)
+# @stats-account-invalid: #optional whether to include invalid
+# operations when computing last access statistics
+# (default: true) (Since 2.5)
+# @stats-account-failed: #optional whether to include failed
+# operations when computing latency and last
+# access statistics (default: true) (Since 2.5)
# @detect-zeroes: #optional detect and optimize zero writes (Since 2.1)
# (default: off)
#
@@ -1448,6 +1461,8 @@
'*rerror': 'BlockdevOnError',
'*werror': 'BlockdevOnError',
'*read-only': 'bool',
+ '*stats-account-invalid': 'bool',
+ '*stats-account-failed': 'bool',
'*detect-zeroes': 'BlockdevDetectZeroesOptions' } }
##
diff --git a/qmp-commands.hx b/qmp-commands.hx
index 65936bd..57bad44 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -2542,6 +2542,11 @@ Each json-object contain the following:
(json-int)
- "invalid_flush_operations": number of invalid flush operations
(json-int)
+ - "account_invalid": whether invalid operations are included in
+ the last access statistics (json-bool)
+ - "account_failed": whether failed operations are included in the
+ latency and last access statistics
+ (json-bool)
- "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
@@ -2567,7 +2572,9 @@ Example:
"flush_operations":61,
"rd_merged":0,
"wr_merged":0,
- "idle_time_ns":2953431879
+ "idle_time_ns":2953431879,
+ "account_invalid":true,
+ "account_failed":false
}
},
"stats":{
@@ -2582,7 +2589,9 @@ Example:
"flush_total_times_ns":49653,
"rd_merged":0,
"wr_merged":0,
- "idle_time_ns":2953431879
+ "idle_time_ns":2953431879,
+ "account_invalid":true,
+ "account_failed":false
}
},
{
@@ -2598,7 +2607,9 @@ Example:
"rd_total_times_ns":0
"flush_total_times_ns":0,
"rd_merged":0,
- "wr_merged":0
+ "wr_merged":0,
+ "account_invalid":false,
+ "account_failed":false
}
},
{
@@ -2614,7 +2625,9 @@ Example:
"rd_total_times_ns":0
"flush_total_times_ns":0,
"rd_merged":0,
- "wr_merged":0
+ "wr_merged":0,
+ "account_invalid":false,
+ "account_failed":false
}
},
{
@@ -2630,7 +2643,9 @@ Example:
"rd_total_times_ns":0
"flush_total_times_ns":0,
"rd_merged":0,
- "wr_merged":0
+ "wr_merged":0,
+ "account_invalid":false,
+ "account_failed":false
}
}
]
--
2.6.1
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [Qemu-devel] [PATCH v4 08/21] block: Compute minimum, maximum and average I/O latencies
2015-10-28 15:32 [Qemu-devel] [PATCH v4 00/21] Extended I/O accounting Alberto Garcia
` (6 preceding siblings ...)
2015-10-28 15:33 ` [Qemu-devel] [PATCH v4 07/21] block: Allow configuring whether to account failed and invalid ops Alberto Garcia
@ 2015-10-28 15:33 ` Alberto Garcia
2015-10-28 15:33 ` [Qemu-devel] [PATCH v4 09/21] block: Add average I/O queue depth to BlockDeviceTimedStats Alberto Garcia
` (13 subsequent siblings)
21 siblings, 0 replies; 24+ messages in thread
From: Alberto Garcia @ 2015-10-28 15:33 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Alberto Garcia, qemu-block, Markus Armbruster,
Max Reitz, Stefan Hajnoczi
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>
---
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 57bad44..8761916 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.6.1
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [Qemu-devel] [PATCH v4 09/21] block: Add average I/O queue depth to BlockDeviceTimedStats
2015-10-28 15:32 [Qemu-devel] [PATCH v4 00/21] Extended I/O accounting Alberto Garcia
` (7 preceding siblings ...)
2015-10-28 15:33 ` [Qemu-devel] [PATCH v4 08/21] block: Compute minimum, maximum and average I/O latencies Alberto Garcia
@ 2015-10-28 15:33 ` Alberto Garcia
2015-10-28 15:33 ` [Qemu-devel] [PATCH v4 10/21] block: New option to define the intervals for collecting I/O statistics Alberto Garcia
` (12 subsequent siblings)
21 siblings, 0 replies; 24+ messages in thread
From: Alberto Garcia @ 2015-10-28 15:33 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Alberto Garcia, qemu-block, Markus Armbruster,
Max Reitz, Stefan Hajnoczi
This patch adds two new fields to BlockDeviceTimedStats that track the
average number of pending read and write requests for a block device.
The values are calculated for the period of time defined for that
interval.
Signed-off-by: Alberto Garcia <berto@igalia.com>
---
block/accounting.c | 12 ++++++++++++
block/qapi.c | 5 +++++
include/block/accounting.h | 2 ++
include/qemu/timed-average.h | 1 +
qapi/block-core.json | 9 ++++++++-
qmp-commands.hx | 6 ++++++
util/timed-average.c | 31 ++++++++++++++++++++++++++-----
7 files changed, 60 insertions(+), 6 deletions(-)
diff --git a/block/accounting.c b/block/accounting.c
index 61de8ce..a941931 100644
--- a/block/accounting.c
+++ b/block/accounting.c
@@ -143,3 +143,15 @@ int64_t block_acct_idle_time_ns(BlockAcctStats *stats)
{
return qemu_clock_get_ns(clock_type) - stats->last_access_time_ns;
}
+
+double block_acct_queue_depth(BlockAcctTimedStats *stats,
+ enum BlockAcctType type)
+{
+ uint64_t sum, elapsed;
+
+ assert(type < BLOCK_MAX_IOTYPE);
+
+ sum = timed_average_sum(&stats->latency[type], &elapsed);
+
+ return (double) sum / elapsed;
+}
diff --git a/block/qapi.c b/block/qapi.c
index 4baf6e1..99d5303 100644
--- a/block/qapi.c
+++ b/block/qapi.c
@@ -402,6 +402,11 @@ static BlockStats *bdrv_query_stats(const BlockDriverState *bs,
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);
+
+ dev_stats->avg_rd_queue_depth =
+ block_acct_queue_depth(ts, BLOCK_ACCT_READ);
+ dev_stats->avg_wr_queue_depth =
+ block_acct_queue_depth(ts, BLOCK_ACCT_WRITE);
}
}
diff --git a/include/block/accounting.h b/include/block/accounting.h
index 1dd582a..482926b 100644
--- a/include/block/accounting.h
+++ b/include/block/accounting.h
@@ -78,5 +78,7 @@ void block_acct_invalid(BlockAcctStats *stats, enum BlockAcctType type);
void block_acct_merge_done(BlockAcctStats *stats, enum BlockAcctType type,
int num_requests);
int64_t block_acct_idle_time_ns(BlockAcctStats *stats);
+double block_acct_queue_depth(BlockAcctTimedStats *stats,
+ enum BlockAcctType type);
#endif
diff --git a/include/qemu/timed-average.h b/include/qemu/timed-average.h
index f1cdddc..364bf88 100644
--- a/include/qemu/timed-average.h
+++ b/include/qemu/timed-average.h
@@ -59,5 +59,6 @@ void timed_average_account(TimedAverage *ta, uint64_t value);
uint64_t timed_average_min(TimedAverage *ta);
uint64_t timed_average_avg(TimedAverage *ta);
uint64_t timed_average_max(TimedAverage *ta);
+uint64_t timed_average_sum(TimedAverage *ta, uint64_t *elapsed);
#endif
diff --git a/qapi/block-core.json b/qapi/block-core.json
index e2cfac6..0742794 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -450,6 +450,12 @@
# @avg_flush_latency_ns: Average latency of flush operations in the
# defined interval, in nanoseconds.
#
+# @avg_rd_queue_depth: Average number of pending read operations
+# in the defined interval.
+#
+# @avg_wr_queue_depth: Average number of pending write operations
+# in the defined interval.
+#
# Since: 2.5
##
@@ -458,7 +464,8 @@
'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' } }
+ 'max_flush_latency_ns': 'int', 'avg_flush_latency_ns': 'int',
+ 'avg_rd_queue_depth': 'number', 'avg_wr_queue_depth': 'number' } }
##
# @BlockDeviceStats:
diff --git a/qmp-commands.hx b/qmp-commands.hx
index 8761916..afcf609 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -2578,6 +2578,12 @@ Each json-object contain the following:
- "avg_flush_latency_ns": average latency of flush operations
in the defined interval, in
nanoseconds (json-int)
+ - "avg_rd_queue_depth": average number of pending read
+ operations in the defined interval
+ (json-number)
+ - "avg_wr_queue_depth": average number of pending write
+ operations in the defined interval
+ (json-number).
- "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
diff --git a/util/timed-average.c b/util/timed-average.c
index 98a1170..a2dfb48 100644
--- a/util/timed-average.c
+++ b/util/timed-average.c
@@ -120,8 +120,10 @@ void timed_average_init(TimedAverage *ta, QEMUClockType clock_type,
* expiration time if that's the case.
*
* @ta: the TimedAverage structure
+ * @elapsed: if non-NULL, the elapsed time (in ns) within the current
+ * window will be stored here
*/
-static void check_expirations(TimedAverage *ta)
+static void check_expirations(TimedAverage *ta, uint64_t *elapsed)
{
int64_t now = qemu_clock_get_ns(ta->clock_type);
int i;
@@ -143,6 +145,12 @@ static void check_expirations(TimedAverage *ta)
} else {
ta->current = 1;
}
+
+ /* Calculate the elapsed time within the current window */
+ if (elapsed) {
+ int64_t remaining = ta->windows[ta->current].expiration - now;
+ *elapsed = ta->period - remaining;
+ }
}
/* Account a value
@@ -153,7 +161,7 @@ static void check_expirations(TimedAverage *ta)
void timed_average_account(TimedAverage *ta, uint64_t value)
{
int i;
- check_expirations(ta);
+ check_expirations(ta, NULL);
/* Do the accounting in both windows at the same time */
for (i = 0; i < 2; i++) {
@@ -180,7 +188,7 @@ void timed_average_account(TimedAverage *ta, uint64_t value)
uint64_t timed_average_min(TimedAverage *ta)
{
TimedAverageWindow *w;
- check_expirations(ta);
+ check_expirations(ta, NULL);
w = current_window(ta);
return w->min < UINT64_MAX ? w->min : 0;
}
@@ -193,7 +201,7 @@ uint64_t timed_average_min(TimedAverage *ta)
uint64_t timed_average_avg(TimedAverage *ta)
{
TimedAverageWindow *w;
- check_expirations(ta);
+ check_expirations(ta, NULL);
w = current_window(ta);
return w->count > 0 ? w->sum / w->count : 0;
}
@@ -205,6 +213,19 @@ uint64_t timed_average_avg(TimedAverage *ta)
*/
uint64_t timed_average_max(TimedAverage *ta)
{
- check_expirations(ta);
+ check_expirations(ta, NULL);
return current_window(ta)->max;
}
+
+/* Get the sum of all accounted values
+ * @ta: the TimedAverage structure
+ * @elapsed: if non-NULL, the elapsed time (in ns) will be stored here
+ * @ret: the sum of all accounted values
+ */
+uint64_t timed_average_sum(TimedAverage *ta, uint64_t *elapsed)
+{
+ TimedAverageWindow *w;
+ check_expirations(ta, elapsed);
+ w = current_window(ta);
+ return w->sum;
+}
--
2.6.1
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [Qemu-devel] [PATCH v4 10/21] block: New option to define the intervals for collecting I/O statistics
2015-10-28 15:32 [Qemu-devel] [PATCH v4 00/21] Extended I/O accounting Alberto Garcia
` (8 preceding siblings ...)
2015-10-28 15:33 ` [Qemu-devel] [PATCH v4 09/21] block: Add average I/O queue depth to BlockDeviceTimedStats Alberto Garcia
@ 2015-10-28 15:33 ` Alberto Garcia
2015-11-10 17:27 ` Eric Blake
2015-10-28 15:33 ` [Qemu-devel] [PATCH v4 11/21] qemu-io: Account for failed, invalid and flush operations Alberto Garcia
` (11 subsequent siblings)
21 siblings, 1 reply; 24+ messages in thread
From: Alberto Garcia @ 2015-10-28 15:33 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Alberto Garcia, qemu-block, Markus Armbruster,
Max Reitz, Stefan Hajnoczi
The BlockAcctStats structure contains a list of BlockAcctTimedStats.
Each one of these collects statistics about the minimum, maximum and
average latencies of all I/O operations in a certain interval of time.
This patch adds a new "stats-intervals" option that allows defining
these intervals.
Signed-off-by: Alberto Garcia <berto@igalia.com>
---
blockdev.c | 37 +++++++++++++++++++++++++++++++++++++
qapi/block-core.json | 4 ++++
2 files changed, 41 insertions(+)
diff --git a/blockdev.c b/blockdev.c
index 82cd6a0..b060385 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -468,6 +468,7 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
int bdrv_flags = 0;
int on_read_error, on_write_error;
bool account_invalid, account_failed;
+ const char *stats_intervals;
BlockBackend *blk;
BlockDriverState *bs;
ThrottleConfig cfg;
@@ -507,6 +508,8 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
account_invalid = qemu_opt_get_bool(opts, "stats-account-invalid", true);
account_failed = qemu_opt_get_bool(opts, "stats-account-failed", true);
+ stats_intervals = qemu_opt_get(opts, "stats-intervals");
+
extract_common_blockdev_options(opts, &bdrv_flags, &throttling_group, &cfg,
&detect_zeroes, &error);
if (error) {
@@ -605,6 +608,35 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
}
block_acct_init(blk_get_stats(blk), account_invalid, account_failed);
+
+ if (stats_intervals) {
+ char **intervals = g_strsplit(stats_intervals, ":", 0);
+ unsigned i;
+
+ if (*stats_intervals == '\0') {
+ error_setg(&error, "stats-intervals can't have an empty value");
+ }
+
+ for (i = 0; !error && intervals[i] != NULL; i++) {
+ unsigned long long val;
+ if (parse_uint_full(intervals[i], &val, 10) == 0 &&
+ val > 0 && val <= UINT_MAX) {
+ block_acct_add_interval(blk_get_stats(blk), val);
+ } else {
+ error_setg(&error, "Invalid interval length: '%s'",
+ intervals[i]);
+ }
+ }
+
+ g_strfreev(intervals);
+
+ if (error) {
+ error_propagate(errp, error);
+ blk_unref(blk);
+ blk = NULL;
+ goto err_no_bs_opts;
+ }
+ }
}
blk_set_on_error(blk, on_read_error, on_write_error);
@@ -3420,6 +3452,11 @@ QemuOptsList qemu_common_drive_opts = {
.type = QEMU_OPT_BOOL,
.help = "whether to account for failed I/O operations "
"in the statistics",
+ },{
+ .name = "stats-intervals",
+ .type = QEMU_OPT_STRING,
+ .help = "colon-separated list of intervals "
+ "for collecting I/O statistics, in seconds",
},
{ /* end of list */ }
},
diff --git a/qapi/block-core.json b/qapi/block-core.json
index 0742794..273d073 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -1503,6 +1503,9 @@
# @stats-account-failed: #optional whether to include failed
# operations when computing latency and last
# access statistics (default: true) (Since 2.5)
+# @stats-intervals: #optional colon-separated list of intervals for
+# collecting I/O statistics, in seconds (default: none)
+# (Since 2.5)
# @detect-zeroes: #optional detect and optimize zero writes (Since 2.1)
# (default: off)
#
@@ -1520,6 +1523,7 @@
'*read-only': 'bool',
'*stats-account-invalid': 'bool',
'*stats-account-failed': 'bool',
+ '*stats-intervals': 'str',
'*detect-zeroes': 'BlockdevDetectZeroesOptions' } }
##
--
2.6.1
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [Qemu-devel] [PATCH v4 11/21] qemu-io: Account for failed, invalid and flush operations
2015-10-28 15:32 [Qemu-devel] [PATCH v4 00/21] Extended I/O accounting Alberto Garcia
` (9 preceding siblings ...)
2015-10-28 15:33 ` [Qemu-devel] [PATCH v4 10/21] block: New option to define the intervals for collecting I/O statistics Alberto Garcia
@ 2015-10-28 15:33 ` Alberto Garcia
2015-10-28 15:33 ` [Qemu-devel] [PATCH v4 12/21] block: Use QEMU_CLOCK_VIRTUAL for the accounting code in qtest mode Alberto Garcia
` (10 subsequent siblings)
21 siblings, 0 replies; 24+ messages in thread
From: Alberto Garcia @ 2015-10-28 15:33 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Alberto Garcia, qemu-block, Markus Armbruster,
Max Reitz, Stefan Hajnoczi
Signed-off-by: Alberto Garcia <berto@igalia.com>
---
qemu-io-cmds.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c
index 6e5d1e4..0cac623 100644
--- a/qemu-io-cmds.c
+++ b/qemu-io-cmds.c
@@ -1364,6 +1364,7 @@ static void aio_write_done(void *opaque, int ret)
if (ret < 0) {
printf("aio_write failed: %s\n", strerror(-ret));
+ block_acct_failed(blk_get_stats(ctx->blk), &ctx->acct);
goto out;
}
@@ -1392,6 +1393,7 @@ static void aio_read_done(void *opaque, int ret)
if (ret < 0) {
printf("readv failed: %s\n", strerror(-ret));
+ block_acct_failed(blk_get_stats(ctx->blk), &ctx->acct);
goto out;
}
@@ -1505,6 +1507,7 @@ static int aio_read_f(BlockBackend *blk, int argc, char **argv)
if (ctx->offset & 0x1ff) {
printf("offset %" PRId64 " is not sector aligned\n",
ctx->offset);
+ block_acct_invalid(blk_get_stats(blk), BLOCK_ACCT_READ);
g_free(ctx);
return 0;
}
@@ -1512,6 +1515,7 @@ static int aio_read_f(BlockBackend *blk, int argc, char **argv)
nr_iov = argc - optind;
ctx->buf = create_iovec(blk, &ctx->qiov, &argv[optind], nr_iov, 0xab);
if (ctx->buf == NULL) {
+ block_acct_invalid(blk_get_stats(blk), BLOCK_ACCT_READ);
g_free(ctx);
return 0;
}
@@ -1600,6 +1604,7 @@ static int aio_write_f(BlockBackend *blk, int argc, char **argv)
if (ctx->offset & 0x1ff) {
printf("offset %" PRId64 " is not sector aligned\n",
ctx->offset);
+ block_acct_invalid(blk_get_stats(blk), BLOCK_ACCT_WRITE);
g_free(ctx);
return 0;
}
@@ -1607,6 +1612,7 @@ static int aio_write_f(BlockBackend *blk, int argc, char **argv)
nr_iov = argc - optind;
ctx->buf = create_iovec(blk, &ctx->qiov, &argv[optind], nr_iov, pattern);
if (ctx->buf == NULL) {
+ block_acct_invalid(blk_get_stats(blk), BLOCK_ACCT_WRITE);
g_free(ctx);
return 0;
}
@@ -1621,7 +1627,10 @@ static int aio_write_f(BlockBackend *blk, int argc, char **argv)
static int aio_flush_f(BlockBackend *blk, int argc, char **argv)
{
+ BlockAcctCookie cookie;
+ block_acct_start(blk_get_stats(blk), &cookie, 0, BLOCK_ACCT_FLUSH);
blk_drain_all();
+ block_acct_done(blk_get_stats(blk), &cookie);
return 0;
}
--
2.6.1
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [Qemu-devel] [PATCH v4 12/21] block: Use QEMU_CLOCK_VIRTUAL for the accounting code in qtest mode
2015-10-28 15:32 [Qemu-devel] [PATCH v4 00/21] Extended I/O accounting Alberto Garcia
` (10 preceding siblings ...)
2015-10-28 15:33 ` [Qemu-devel] [PATCH v4 11/21] qemu-io: Account for failed, invalid and flush operations Alberto Garcia
@ 2015-10-28 15:33 ` Alberto Garcia
2015-10-28 15:33 ` [Qemu-devel] [PATCH v4 13/21] iotests: Add test for the block device statistics Alberto Garcia
` (9 subsequent siblings)
21 siblings, 0 replies; 24+ messages in thread
From: Alberto Garcia @ 2015-10-28 15:33 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Alberto Garcia, qemu-block, Markus Armbruster,
Max Reitz, Stefan Hajnoczi
This patch switches to QEMU_CLOCK_VIRTUAL for the accounting code in
qtest mode, and makes the latency of the operation constant. This way we
can perform tests on the accounting code with reproducible results.
Signed-off-by: Alberto Garcia <berto@igalia.com>
---
block/accounting.c | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/block/accounting.c b/block/accounting.c
index a941931..05a5c5f 100644
--- a/block/accounting.c
+++ b/block/accounting.c
@@ -25,14 +25,20 @@
#include "block/accounting.h"
#include "block/block_int.h"
#include "qemu/timer.h"
+#include "sysemu/qtest.h"
static QEMUClockType clock_type = QEMU_CLOCK_REALTIME;
+static const int qtest_latency_ns = NANOSECONDS_PER_SECOND / 1000;
void block_acct_init(BlockAcctStats *stats, bool account_invalid,
bool account_failed)
{
stats->account_invalid = account_invalid;
stats->account_failed = account_failed;
+
+ if (qtest_enabled()) {
+ clock_type = QEMU_CLOCK_VIRTUAL;
+ }
}
void block_acct_cleanup(BlockAcctStats *stats)
@@ -84,6 +90,10 @@ void block_acct_done(BlockAcctStats *stats, BlockAcctCookie *cookie)
int64_t time_ns = qemu_clock_get_ns(clock_type);
int64_t latency_ns = time_ns - cookie->start_time_ns;
+ if (qtest_enabled()) {
+ latency_ns = qtest_latency_ns;
+ }
+
assert(cookie->type < BLOCK_MAX_IOTYPE);
stats->nr_bytes[cookie->type] += cookie->bytes;
@@ -107,6 +117,10 @@ void block_acct_failed(BlockAcctStats *stats, BlockAcctCookie *cookie)
int64_t time_ns = qemu_clock_get_ns(clock_type);
int64_t latency_ns = time_ns - cookie->start_time_ns;
+ if (qtest_enabled()) {
+ latency_ns = qtest_latency_ns;
+ }
+
stats->total_time_ns[cookie->type] += latency_ns;
stats->last_access_time_ns = time_ns;
--
2.6.1
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [Qemu-devel] [PATCH v4 13/21] iotests: Add test for the block device statistics
2015-10-28 15:32 [Qemu-devel] [PATCH v4 00/21] Extended I/O accounting Alberto Garcia
` (11 preceding siblings ...)
2015-10-28 15:33 ` [Qemu-devel] [PATCH v4 12/21] block: Use QEMU_CLOCK_VIRTUAL for the accounting code in qtest mode Alberto Garcia
@ 2015-10-28 15:33 ` Alberto Garcia
2015-10-28 15:33 ` [Qemu-devel] [PATCH v4 14/21] nvme: Account for failed and invalid operations Alberto Garcia
` (8 subsequent siblings)
21 siblings, 0 replies; 24+ messages in thread
From: Alberto Garcia @ 2015-10-28 15:33 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Alberto Garcia, qemu-block, Markus Armbruster,
Max Reitz, Stefan Hajnoczi
Signed-off-by: Alberto Garcia <berto@igalia.com>
---
tests/qemu-iotests/136 | 349 +++++++++++++++++++++++++++++++++++++++++++++
tests/qemu-iotests/136.out | 5 +
tests/qemu-iotests/group | 1 +
3 files changed, 355 insertions(+)
create mode 100644 tests/qemu-iotests/136
create mode 100644 tests/qemu-iotests/136.out
diff --git a/tests/qemu-iotests/136 b/tests/qemu-iotests/136
new file mode 100644
index 0000000..f574d83
--- /dev/null
+++ b/tests/qemu-iotests/136
@@ -0,0 +1,349 @@
+#!/usr/bin/env python
+#
+# Tests for block device statistics
+#
+# Copyright (C) 2015 Igalia, S.L.
+# Author: Alberto Garcia <berto@igalia.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+import iotests
+import os
+
+interval_length = 10
+nsec_per_sec = 1000000000
+op_latency = nsec_per_sec / 1000 # See qtest_latency_ns in accounting.c
+bad_sector = 8192
+bad_offset = bad_sector * 512
+blkdebug_file = os.path.join(iotests.test_dir, 'blkdebug.conf')
+
+class BlockDeviceStatsTestCase(iotests.QMPTestCase):
+ test_img = "null-aio://"
+ total_rd_bytes = 0
+ total_rd_ops = 0
+ total_wr_bytes = 0
+ total_wr_ops = 0
+ total_wr_merged = 0
+ total_flush_ops = 0
+ failed_rd_ops = 0
+ failed_wr_ops = 0
+ invalid_rd_ops = 0
+ invalid_wr_ops = 0
+ wr_highest_offset = 0
+ account_invalid = False
+ account_failed = False
+
+ def blockstats(self, device):
+ result = self.vm.qmp("query-blockstats")
+ for r in result['return']:
+ if r['device'] == device:
+ return r['stats']
+ raise Exception("Device not found for blockstats: %s" % device)
+
+ def create_blkdebug_file(self):
+ file = open(blkdebug_file, 'w')
+ file.write('''
+[inject-error]
+event = "read_aio"
+errno = "5"
+sector = "%d"
+
+[inject-error]
+event = "write_aio"
+errno = "5"
+sector = "%d"
+''' % (bad_sector, bad_sector))
+ file.close()
+
+ def setUp(self):
+ drive_args = []
+ drive_args.append("stats-intervals=%d" % interval_length)
+ drive_args.append("stats-account-invalid=%s" %
+ (self.account_invalid and "on" or "off"))
+ drive_args.append("stats-account-failed=%s" %
+ (self.account_failed and "on" or "off"))
+ self.create_blkdebug_file()
+ self.vm = iotests.VM().add_drive('blkdebug:%s:%s ' %
+ (blkdebug_file, self.test_img),
+ ','.join(drive_args))
+ self.vm.launch()
+ # Set an initial value for the clock
+ self.vm.qtest("clock_step %d" % nsec_per_sec)
+
+ def tearDown(self):
+ self.vm.shutdown()
+ os.remove(blkdebug_file)
+
+ def accounted_ops(self, read = False, write = False, flush = False):
+ ops = 0
+ if write:
+ ops += self.total_wr_ops
+ if self.account_failed:
+ ops += self.failed_wr_ops
+ if self.account_invalid:
+ ops += self.invalid_wr_ops
+ if read:
+ ops += self.total_rd_ops
+ if self.account_failed:
+ ops += self.failed_rd_ops
+ if self.account_invalid:
+ ops += self.invalid_rd_ops
+ if flush:
+ ops += self.total_flush_ops
+ return ops
+
+ def accounted_latency(self, read = False, write = False, flush = False):
+ latency = 0
+ if write:
+ latency += self.total_wr_ops * op_latency
+ if self.account_failed:
+ latency += self.failed_wr_ops * op_latency
+ if read:
+ latency += self.total_rd_ops * op_latency
+ if self.account_failed:
+ latency += self.failed_rd_ops * op_latency
+ if flush:
+ latency += self.total_flush_ops * op_latency
+ return latency
+
+ def check_values(self):
+ stats = self.blockstats('drive0')
+
+ # Check that the totals match with what we have calculated
+ self.assertEqual(self.total_rd_bytes, stats['rd_bytes'])
+ self.assertEqual(self.total_wr_bytes, stats['wr_bytes'])
+ self.assertEqual(self.total_rd_ops, stats['rd_operations'])
+ self.assertEqual(self.total_wr_ops, stats['wr_operations'])
+ self.assertEqual(self.total_flush_ops, stats['flush_operations'])
+ self.assertEqual(self.wr_highest_offset, stats['wr_highest_offset'])
+ self.assertEqual(self.failed_rd_ops, stats['failed_rd_operations'])
+ self.assertEqual(self.failed_wr_ops, stats['failed_wr_operations'])
+ self.assertEqual(self.invalid_rd_ops, stats['invalid_rd_operations'])
+ self.assertEqual(self.invalid_wr_ops, stats['invalid_wr_operations'])
+ self.assertEqual(self.account_invalid, stats['account_invalid'])
+ self.assertEqual(self.account_failed, stats['account_failed'])
+ self.assertEqual(self.total_wr_merged, stats['wr_merged'])
+
+ # Check that there's exactly one interval with the length we defined
+ self.assertEqual(1, len(stats['timed_stats']))
+ timed_stats = stats['timed_stats'][0]
+ self.assertEqual(interval_length, timed_stats['interval_length'])
+
+ total_rd_latency = self.accounted_latency(read = True)
+ if (total_rd_latency != 0):
+ self.assertEqual(total_rd_latency, stats['rd_total_time_ns'])
+ self.assertEqual(op_latency, timed_stats['min_rd_latency_ns'])
+ self.assertEqual(op_latency, timed_stats['max_rd_latency_ns'])
+ self.assertEqual(op_latency, timed_stats['avg_rd_latency_ns'])
+ self.assertLess(0, timed_stats['avg_rd_queue_depth'])
+ else:
+ self.assertEqual(0, stats['rd_total_time_ns'])
+ self.assertEqual(0, timed_stats['min_rd_latency_ns'])
+ self.assertEqual(0, timed_stats['max_rd_latency_ns'])
+ self.assertEqual(0, timed_stats['avg_rd_latency_ns'])
+ self.assertEqual(0, timed_stats['avg_rd_queue_depth'])
+
+ # min read latency <= avg read latency <= max read latency
+ self.assertLessEqual(timed_stats['min_rd_latency_ns'],
+ timed_stats['avg_rd_latency_ns'])
+ self.assertLessEqual(timed_stats['avg_rd_latency_ns'],
+ timed_stats['max_rd_latency_ns'])
+
+ total_wr_latency = self.accounted_latency(write = True)
+ if (total_wr_latency != 0):
+ self.assertEqual(total_wr_latency, stats['wr_total_time_ns'])
+ self.assertEqual(op_latency, timed_stats['min_wr_latency_ns'])
+ self.assertEqual(op_latency, timed_stats['max_wr_latency_ns'])
+ self.assertEqual(op_latency, timed_stats['avg_wr_latency_ns'])
+ self.assertLess(0, timed_stats['avg_wr_queue_depth'])
+ else:
+ self.assertEqual(0, stats['wr_total_time_ns'])
+ self.assertEqual(0, timed_stats['min_wr_latency_ns'])
+ self.assertEqual(0, timed_stats['max_wr_latency_ns'])
+ self.assertEqual(0, timed_stats['avg_wr_latency_ns'])
+ self.assertEqual(0, timed_stats['avg_wr_queue_depth'])
+
+ # min write latency <= avg write latency <= max write latency
+ self.assertLessEqual(timed_stats['min_wr_latency_ns'],
+ timed_stats['avg_wr_latency_ns'])
+ self.assertLessEqual(timed_stats['avg_wr_latency_ns'],
+ timed_stats['max_wr_latency_ns'])
+
+ total_flush_latency = self.accounted_latency(flush = True)
+ if (total_flush_latency != 0):
+ self.assertEqual(total_flush_latency, stats['flush_total_time_ns'])
+ self.assertEqual(op_latency, timed_stats['min_flush_latency_ns'])
+ self.assertEqual(op_latency, timed_stats['max_flush_latency_ns'])
+ self.assertEqual(op_latency, timed_stats['avg_flush_latency_ns'])
+ else:
+ self.assertEqual(0, stats['flush_total_time_ns'])
+ self.assertEqual(0, timed_stats['min_flush_latency_ns'])
+ self.assertEqual(0, timed_stats['max_flush_latency_ns'])
+ self.assertEqual(0, timed_stats['avg_flush_latency_ns'])
+
+ # min flush latency <= avg flush latency <= max flush latency
+ self.assertLessEqual(timed_stats['min_flush_latency_ns'],
+ timed_stats['avg_flush_latency_ns'])
+ self.assertLessEqual(timed_stats['avg_flush_latency_ns'],
+ timed_stats['max_flush_latency_ns'])
+
+ # idle_time_ns must be > 0 if we have performed any operation
+ if (self.accounted_ops(read = True, write = True, flush = True) != 0):
+ self.assertLess(0, stats['idle_time_ns'])
+ else:
+ self.assertFalse(stats.has_key('idle_time_ns'))
+
+ # This test does not alter these, so they must be all 0
+ self.assertEqual(0, stats['rd_merged'])
+ self.assertEqual(0, stats['failed_flush_operations'])
+ self.assertEqual(0, stats['invalid_flush_operations'])
+
+ def do_test_stats(self, rd_size = 0, rd_ops = 0, wr_size = 0, wr_ops = 0,
+ flush_ops = 0, invalid_rd_ops = 0, invalid_wr_ops = 0,
+ failed_rd_ops = 0, failed_wr_ops = 0, wr_merged = 0):
+ # The 'ops' list will contain all the requested I/O operations
+ ops = []
+ for i in range(rd_ops):
+ ops.append("aio_read %d %d" % (i * rd_size, rd_size))
+
+ for i in range(wr_ops):
+ ops.append("aio_write %d %d" % (i * wr_size, wr_size))
+
+ for i in range(flush_ops):
+ ops.append("aio_flush")
+
+ highest_offset = wr_ops * wr_size
+
+ # Two types of invalid operations: unaligned length and unaligned offset
+ for i in range(invalid_rd_ops / 2):
+ ops.append("aio_read 0 511")
+
+ for i in range(invalid_rd_ops / 2, invalid_rd_ops):
+ ops.append("aio_read 13 512")
+
+ for i in range(invalid_wr_ops / 2):
+ ops.append("aio_write 0 511")
+
+ for i in range(invalid_wr_ops / 2, invalid_wr_ops):
+ ops.append("aio_write 13 512")
+
+ for i in range(failed_rd_ops):
+ ops.append("aio_read %d 512" % bad_offset)
+
+ for i in range(failed_wr_ops):
+ ops.append("aio_write %d 512" % bad_offset)
+
+ if failed_wr_ops > 0:
+ highest_offset = max(highest_offset, bad_offset + 512)
+
+ for i in range(wr_merged):
+ first = i * wr_size * 2
+ second = first + wr_size
+ ops.append("multiwrite %d %d ; %d %d" %
+ (first, wr_size, second, wr_size))
+
+ highest_offset = max(highest_offset, wr_merged * wr_size * 2)
+
+ # Now perform all operations
+ for op in ops:
+ self.vm.hmp_qemu_io("drive0", op)
+
+ # Update the expected totals
+ self.total_rd_bytes += rd_ops * rd_size
+ self.total_rd_ops += rd_ops
+ self.total_wr_bytes += wr_ops * wr_size
+ self.total_wr_ops += wr_ops
+ self.total_wr_merged += wr_merged
+ self.total_flush_ops += flush_ops
+ self.invalid_rd_ops += invalid_rd_ops
+ self.invalid_wr_ops += invalid_wr_ops
+ self.failed_rd_ops += failed_rd_ops
+ self.failed_wr_ops += failed_wr_ops
+
+ self.wr_highest_offset = max(self.wr_highest_offset, highest_offset)
+
+ # Advance the clock so idle_time_ns has a meaningful value
+ self.vm.qtest("clock_step %d" % nsec_per_sec)
+
+ # And check that the actual statistics match the expected ones
+ self.check_values()
+
+ def test_read_only(self):
+ test_values = [[512, 1],
+ [65536, 1],
+ [512, 12],
+ [65536, 12]]
+ for i in test_values:
+ self.do_test_stats(rd_size = i[0], rd_ops = i[1])
+
+ def test_write_only(self):
+ test_values = [[512, 1],
+ [65536, 1],
+ [512, 12],
+ [65536, 12]]
+ for i in test_values:
+ self.do_test_stats(wr_size = i[0], wr_ops = i[1])
+
+ def test_invalid(self):
+ self.do_test_stats(invalid_rd_ops = 7)
+ self.do_test_stats(invalid_wr_ops = 3)
+ self.do_test_stats(invalid_rd_ops = 4, invalid_wr_ops = 5)
+
+ def test_failed(self):
+ self.do_test_stats(failed_rd_ops = 8)
+ self.do_test_stats(failed_wr_ops = 6)
+ self.do_test_stats(failed_rd_ops = 5, failed_wr_ops = 12)
+
+ def test_flush(self):
+ self.do_test_stats(flush_ops = 8)
+
+ def test_merged(self):
+ for i in range(5):
+ self.do_test_stats(wr_merged = i * 3)
+
+ def test_all(self):
+ # rd_size, rd_ops, wr_size, wr_ops, flush_ops
+ # invalid_rd_ops, invalid_wr_ops,
+ # failed_rd_ops, failed_wr_ops
+ # wr_merged
+ test_values = [[512, 1, 512, 1, 1, 4, 7, 5, 2, 1],
+ [65536, 1, 2048, 12, 7, 7, 5, 2, 5, 5],
+ [32768, 9, 8192, 1, 4, 3, 2, 4, 6, 4],
+ [16384, 11, 3584, 16, 9, 8, 6, 7, 3, 4]]
+ for i in test_values:
+ self.do_test_stats(*i)
+
+ def test_no_op(self):
+ # All values must be sane before doing any I/O
+ self.check_values()
+
+
+class BlockDeviceStatsTestAccountInvalid(BlockDeviceStatsTestCase):
+ account_invalid = True
+ account_failed = False
+
+class BlockDeviceStatsTestAccountFailed(BlockDeviceStatsTestCase):
+ account_invalid = False
+ account_failed = True
+
+class BlockDeviceStatsTestAccountBoth(BlockDeviceStatsTestCase):
+ account_invalid = True
+ account_failed = True
+
+class BlockDeviceStatsTestCoroutine(BlockDeviceStatsTestCase):
+ test_img = "null-co://"
+
+if __name__ == '__main__':
+ iotests.main(supported_fmts=["raw"])
diff --git a/tests/qemu-iotests/136.out b/tests/qemu-iotests/136.out
new file mode 100644
index 0000000..0a5e958
--- /dev/null
+++ b/tests/qemu-iotests/136.out
@@ -0,0 +1,5 @@
+........................................
+----------------------------------------------------------------------
+Ran 40 tests
+
+OK
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
index 30c784e..71e9961 100644
--- a/tests/qemu-iotests/group
+++ b/tests/qemu-iotests/group
@@ -135,5 +135,6 @@
132 rw auto quick
134 rw auto quick
135 rw auto
+136 rw auto
137 rw auto
138 rw auto quick
--
2.6.1
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [Qemu-devel] [PATCH v4 14/21] nvme: Account for failed and invalid operations
2015-10-28 15:32 [Qemu-devel] [PATCH v4 00/21] Extended I/O accounting Alberto Garcia
` (12 preceding siblings ...)
2015-10-28 15:33 ` [Qemu-devel] [PATCH v4 13/21] iotests: Add test for the block device statistics Alberto Garcia
@ 2015-10-28 15:33 ` Alberto Garcia
2015-10-28 15:33 ` [Qemu-devel] [PATCH v4 15/21] virtio-blk: " Alberto Garcia
` (7 subsequent siblings)
21 siblings, 0 replies; 24+ messages in thread
From: Alberto Garcia @ 2015-10-28 15:33 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Alberto Garcia, qemu-block, Markus Armbruster,
Max Reitz, Stefan Hajnoczi
Signed-off-by: Alberto Garcia <berto@igalia.com>
---
hw/block/nvme.c | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/hw/block/nvme.c b/hw/block/nvme.c
index 5da41b2..169e4fa 100644
--- a/hw/block/nvme.c
+++ b/hw/block/nvme.c
@@ -201,10 +201,11 @@ static void nvme_rw_cb(void *opaque, int ret)
NvmeCtrl *n = sq->ctrl;
NvmeCQueue *cq = n->cq[sq->cqid];
- block_acct_done(blk_get_stats(n->conf.blk), &req->acct);
if (!ret) {
+ block_acct_done(blk_get_stats(n->conf.blk), &req->acct);
req->status = NVME_SUCCESS;
} else {
+ block_acct_failed(blk_get_stats(n->conf.blk), &req->acct);
req->status = NVME_INTERNAL_DEV_ERROR;
}
if (req->has_sg) {
@@ -238,18 +239,22 @@ static uint16_t nvme_rw(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd,
uint64_t data_size = (uint64_t)nlb << data_shift;
uint64_t aio_slba = slba << (data_shift - BDRV_SECTOR_BITS);
int is_write = rw->opcode == NVME_CMD_WRITE ? 1 : 0;
+ enum BlockAcctType acct = is_write ? BLOCK_ACCT_WRITE : BLOCK_ACCT_READ;
if ((slba + nlb) > ns->id_ns.nsze) {
+ block_acct_invalid(blk_get_stats(n->conf.blk), acct);
return NVME_LBA_RANGE | NVME_DNR;
}
+
if (nvme_map_prp(&req->qsg, prp1, prp2, data_size, n)) {
+ block_acct_invalid(blk_get_stats(n->conf.blk), acct);
return NVME_INVALID_FIELD | NVME_DNR;
}
+
assert((nlb << data_shift) == req->qsg.size);
req->has_sg = true;
- dma_acct_start(n->conf.blk, &req->acct, &req->qsg,
- is_write ? BLOCK_ACCT_WRITE : BLOCK_ACCT_READ);
+ dma_acct_start(n->conf.blk, &req->acct, &req->qsg, acct);
req->aiocb = is_write ?
dma_blk_write(n->conf.blk, &req->qsg, aio_slba, nvme_rw_cb, req) :
dma_blk_read(n->conf.blk, &req->qsg, aio_slba, nvme_rw_cb, req);
--
2.6.1
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [Qemu-devel] [PATCH v4 15/21] virtio-blk: Account for failed and invalid operations
2015-10-28 15:32 [Qemu-devel] [PATCH v4 00/21] Extended I/O accounting Alberto Garcia
` (13 preceding siblings ...)
2015-10-28 15:33 ` [Qemu-devel] [PATCH v4 14/21] nvme: Account for failed and invalid operations Alberto Garcia
@ 2015-10-28 15:33 ` Alberto Garcia
2015-10-28 15:33 ` [Qemu-devel] [PATCH v4 16/21] xen_disk: " Alberto Garcia
` (6 subsequent siblings)
21 siblings, 0 replies; 24+ messages in thread
From: Alberto Garcia @ 2015-10-28 15:33 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Alberto Garcia, qemu-block, Markus Armbruster,
Max Reitz, Stefan Hajnoczi
Signed-off-by: Alberto Garcia <berto@igalia.com>
---
hw/block/virtio-blk.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c
index 8beb26b..aea9728 100644
--- a/hw/block/virtio-blk.c
+++ b/hw/block/virtio-blk.c
@@ -76,7 +76,7 @@ static int virtio_blk_handle_rw_error(VirtIOBlockReq *req, int error,
s->rq = req;
} else if (action == BLOCK_ERROR_ACTION_REPORT) {
virtio_blk_req_complete(req, VIRTIO_BLK_S_IOERR);
- block_acct_done(blk_get_stats(s->blk), &req->acct);
+ block_acct_failed(blk_get_stats(s->blk), &req->acct);
virtio_blk_free_request(req);
}
@@ -536,6 +536,8 @@ void virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb)
if (!virtio_blk_sect_range_ok(req->dev, req->sector_num,
req->qiov.size)) {
virtio_blk_req_complete(req, VIRTIO_BLK_S_IOERR);
+ block_acct_invalid(blk_get_stats(req->dev->blk),
+ is_write ? BLOCK_ACCT_WRITE : BLOCK_ACCT_READ);
virtio_blk_free_request(req);
return;
}
--
2.6.1
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [Qemu-devel] [PATCH v4 16/21] xen_disk: Account for failed and invalid operations
2015-10-28 15:32 [Qemu-devel] [PATCH v4 00/21] Extended I/O accounting Alberto Garcia
` (14 preceding siblings ...)
2015-10-28 15:33 ` [Qemu-devel] [PATCH v4 15/21] virtio-blk: " Alberto Garcia
@ 2015-10-28 15:33 ` Alberto Garcia
2015-10-28 15:33 ` [Qemu-devel] [PATCH v4 17/21] atapi: " Alberto Garcia
` (5 subsequent siblings)
21 siblings, 0 replies; 24+ messages in thread
From: Alberto Garcia @ 2015-10-28 15:33 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Alberto Garcia, qemu-block, Markus Armbruster,
Max Reitz, Stefan Hajnoczi
Signed-off-by: Alberto Garcia <berto@igalia.com>
---
hw/block/xen_disk.c | 23 ++++++++++++++++++++++-
1 file changed, 22 insertions(+), 1 deletion(-)
diff --git a/hw/block/xen_disk.c b/hw/block/xen_disk.c
index 4869518..02eda6e 100644
--- a/hw/block/xen_disk.c
+++ b/hw/block/xen_disk.c
@@ -537,7 +537,11 @@ static void qemu_aio_complete(void *opaque, int ret)
break;
}
case BLKIF_OP_READ:
- block_acct_done(blk_get_stats(ioreq->blkdev->blk), &ioreq->acct);
+ if (ioreq->status == BLKIF_RSP_OKAY) {
+ block_acct_done(blk_get_stats(ioreq->blkdev->blk), &ioreq->acct);
+ } else {
+ block_acct_failed(blk_get_stats(ioreq->blkdev->blk), &ioreq->acct);
+ }
break;
case BLKIF_OP_DISCARD:
default:
@@ -722,6 +726,23 @@ static void blk_handle_requests(struct XenBlkDev *blkdev)
/* parse them */
if (ioreq_parse(ioreq) != 0) {
+
+ switch (ioreq->req.operation) {
+ case BLKIF_OP_READ:
+ block_acct_invalid(blk_get_stats(blkdev->blk),
+ BLOCK_ACCT_READ);
+ break;
+ case BLKIF_OP_WRITE:
+ block_acct_invalid(blk_get_stats(blkdev->blk),
+ BLOCK_ACCT_WRITE);
+ break;
+ case BLKIF_OP_FLUSH_DISKCACHE:
+ block_acct_invalid(blk_get_stats(blkdev->blk),
+ BLOCK_ACCT_FLUSH);
+ default:
+ break;
+ };
+
if (blk_send_response_one(ioreq)) {
xen_be_send_notify(&blkdev->xendev);
}
--
2.6.1
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [Qemu-devel] [PATCH v4 17/21] atapi: Account for failed and invalid operations
2015-10-28 15:32 [Qemu-devel] [PATCH v4 00/21] Extended I/O accounting Alberto Garcia
` (15 preceding siblings ...)
2015-10-28 15:33 ` [Qemu-devel] [PATCH v4 16/21] xen_disk: " Alberto Garcia
@ 2015-10-28 15:33 ` Alberto Garcia
2015-10-28 15:33 ` [Qemu-devel] [PATCH v4 18/21] ide: " Alberto Garcia
` (4 subsequent siblings)
21 siblings, 0 replies; 24+ messages in thread
From: Alberto Garcia @ 2015-10-28 15:33 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Alberto Garcia, qemu-block, Markus Armbruster,
Max Reitz, Stefan Hajnoczi
Signed-off-by: Alberto Garcia <berto@igalia.com>
---
hw/ide/atapi.c | 31 +++++++++++++++++++------------
1 file changed, 19 insertions(+), 12 deletions(-)
diff --git a/hw/ide/atapi.c b/hw/ide/atapi.c
index 747f466..cf0b78e 100644
--- a/hw/ide/atapi.c
+++ b/hw/ide/atapi.c
@@ -108,27 +108,30 @@ static void cd_data_to_raw(uint8_t *buf, int lba)
static int cd_read_sector(IDEState *s, int lba, uint8_t *buf, int sector_size)
{
int ret;
+ block_acct_start(blk_get_stats(s->blk), &s->acct,
+ 4 * BDRV_SECTOR_SIZE, BLOCK_ACCT_READ);
switch(sector_size) {
case 2048:
- block_acct_start(blk_get_stats(s->blk), &s->acct,
- 4 * BDRV_SECTOR_SIZE, BLOCK_ACCT_READ);
ret = blk_read(s->blk, (int64_t)lba << 2, buf, 4);
- block_acct_done(blk_get_stats(s->blk), &s->acct);
break;
case 2352:
- block_acct_start(blk_get_stats(s->blk), &s->acct,
- 4 * BDRV_SECTOR_SIZE, BLOCK_ACCT_READ);
ret = blk_read(s->blk, (int64_t)lba << 2, buf + 16, 4);
- block_acct_done(blk_get_stats(s->blk), &s->acct);
- if (ret < 0)
- return ret;
- cd_data_to_raw(buf, lba);
+ if (ret >= 0) {
+ cd_data_to_raw(buf, lba);
+ }
break;
default:
- ret = -EIO;
- break;
+ block_acct_invalid(blk_get_stats(s->blk), BLOCK_ACCT_READ);
+ return -EIO;
}
+
+ if (ret < 0) {
+ block_acct_failed(blk_get_stats(s->blk), &s->acct);
+ } else {
+ block_acct_done(blk_get_stats(s->blk), &s->acct);
+ }
+
return ret;
}
@@ -357,7 +360,11 @@ static void ide_atapi_cmd_read_dma_cb(void *opaque, int ret)
return;
eot:
- block_acct_done(blk_get_stats(s->blk), &s->acct);
+ if (ret < 0) {
+ block_acct_failed(blk_get_stats(s->blk), &s->acct);
+ } else {
+ block_acct_done(blk_get_stats(s->blk), &s->acct);
+ }
ide_set_inactive(s, false);
}
--
2.6.1
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [Qemu-devel] [PATCH v4 18/21] ide: Account for failed and invalid operations
2015-10-28 15:32 [Qemu-devel] [PATCH v4 00/21] Extended I/O accounting Alberto Garcia
` (16 preceding siblings ...)
2015-10-28 15:33 ` [Qemu-devel] [PATCH v4 17/21] atapi: " Alberto Garcia
@ 2015-10-28 15:33 ` Alberto Garcia
2015-10-28 15:33 ` [Qemu-devel] [PATCH v4 19/21] macio: Account for failed operations Alberto Garcia
` (3 subsequent siblings)
21 siblings, 0 replies; 24+ messages in thread
From: Alberto Garcia @ 2015-10-28 15:33 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Alberto Garcia, qemu-block, Markus Armbruster,
Max Reitz, Stefan Hajnoczi
Signed-off-by: Alberto Garcia <berto@igalia.com>
---
hw/ide/core.c | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/hw/ide/core.c b/hw/ide/core.c
index b559f1b..dd7e9c5 100644
--- a/hw/ide/core.c
+++ b/hw/ide/core.c
@@ -574,7 +574,6 @@ static void ide_sector_read_cb(void *opaque, int ret)
if (ret == -ECANCELED) {
return;
}
- block_acct_done(blk_get_stats(s->blk), &s->acct);
if (ret != 0) {
if (ide_handle_rw_error(s, -ret, IDE_RETRY_PIO |
IDE_RETRY_READ)) {
@@ -582,6 +581,8 @@ static void ide_sector_read_cb(void *opaque, int ret)
}
}
+ block_acct_done(blk_get_stats(s->blk), &s->acct);
+
n = s->nsector;
if (n > s->req_nb_sectors) {
n = s->req_nb_sectors;
@@ -621,6 +622,7 @@ static void ide_sector_read(IDEState *s)
if (!ide_sect_range_ok(s, sector_num, n)) {
ide_rw_error(s);
+ block_acct_invalid(blk_get_stats(s->blk), BLOCK_ACCT_READ);
return;
}
@@ -672,6 +674,7 @@ static int ide_handle_rw_error(IDEState *s, int error, int op)
assert(s->bus->retry_unit == s->unit);
s->bus->error_status = op;
} else if (action == BLOCK_ERROR_ACTION_REPORT) {
+ block_acct_failed(blk_get_stats(s->blk), &s->acct);
if (op & IDE_RETRY_DMA) {
ide_dma_error(s);
} else {
@@ -750,6 +753,7 @@ static void ide_dma_cb(void *opaque, int ret)
if ((s->dma_cmd == IDE_DMA_READ || s->dma_cmd == IDE_DMA_WRITE) &&
!ide_sect_range_ok(s, sector_num, n)) {
ide_dma_error(s);
+ block_acct_invalid(blk_get_stats(s->blk), s->acct.type);
return;
}
@@ -826,7 +830,6 @@ static void ide_sector_write_cb(void *opaque, int ret)
if (ret == -ECANCELED) {
return;
}
- block_acct_done(blk_get_stats(s->blk), &s->acct);
s->pio_aiocb = NULL;
s->status &= ~BUSY_STAT;
@@ -837,6 +840,8 @@ static void ide_sector_write_cb(void *opaque, int ret)
}
}
+ block_acct_done(blk_get_stats(s->blk), &s->acct);
+
n = s->nsector;
if (n > s->req_nb_sectors) {
n = s->req_nb_sectors;
@@ -887,6 +892,7 @@ static void ide_sector_write(IDEState *s)
if (!ide_sect_range_ok(s, sector_num, n)) {
ide_rw_error(s);
+ block_acct_invalid(blk_get_stats(s->blk), BLOCK_ACCT_WRITE);
return;
}
--
2.6.1
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [Qemu-devel] [PATCH v4 19/21] macio: Account for failed operations
2015-10-28 15:32 [Qemu-devel] [PATCH v4 00/21] Extended I/O accounting Alberto Garcia
` (17 preceding siblings ...)
2015-10-28 15:33 ` [Qemu-devel] [PATCH v4 18/21] ide: " Alberto Garcia
@ 2015-10-28 15:33 ` Alberto Garcia
2015-10-28 15:33 ` [Qemu-devel] [PATCH v4 20/21] scsi-disk: " Alberto Garcia
` (2 subsequent siblings)
21 siblings, 0 replies; 24+ messages in thread
From: Alberto Garcia @ 2015-10-28 15:33 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Alberto Garcia, qemu-block, Markus Armbruster,
Max Reitz, Stefan Hajnoczi
Signed-off-by: Alberto Garcia <berto@igalia.com>
---
hw/ide/macio.c | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/hw/ide/macio.c b/hw/ide/macio.c
index 893c9b9..3ee962f 100644
--- a/hw/ide/macio.c
+++ b/hw/ide/macio.c
@@ -286,7 +286,11 @@ static void pmac_ide_atapi_transfer_cb(void *opaque, int ret)
return;
done:
- block_acct_done(blk_get_stats(s->blk), &s->acct);
+ if (ret < 0) {
+ block_acct_failed(blk_get_stats(s->blk), &s->acct);
+ } else {
+ block_acct_done(blk_get_stats(s->blk), &s->acct);
+ }
io->dma_end(opaque);
return;
@@ -348,7 +352,11 @@ static void pmac_ide_transfer_cb(void *opaque, int ret)
done:
if (s->dma_cmd == IDE_DMA_READ || s->dma_cmd == IDE_DMA_WRITE) {
- block_acct_done(blk_get_stats(s->blk), &s->acct);
+ if (ret < 0) {
+ block_acct_failed(blk_get_stats(s->blk), &s->acct);
+ } else {
+ block_acct_done(blk_get_stats(s->blk), &s->acct);
+ }
}
io->dma_end(opaque);
}
--
2.6.1
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [Qemu-devel] [PATCH v4 20/21] scsi-disk: Account for failed operations
2015-10-28 15:32 [Qemu-devel] [PATCH v4 00/21] Extended I/O accounting Alberto Garcia
` (18 preceding siblings ...)
2015-10-28 15:33 ` [Qemu-devel] [PATCH v4 19/21] macio: Account for failed operations Alberto Garcia
@ 2015-10-28 15:33 ` Alberto Garcia
2015-10-28 15:33 ` [Qemu-devel] [PATCH v4 21/21] block: Update copyright of the accounting code Alberto Garcia
2015-11-10 15:07 ` [Qemu-devel] [PATCH v4 00/21] Extended I/O accounting Stefan Hajnoczi
21 siblings, 0 replies; 24+ messages in thread
From: Alberto Garcia @ 2015-10-28 15:33 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Alberto Garcia, qemu-block, Markus Armbruster,
Max Reitz, Stefan Hajnoczi
Signed-off-by: Alberto Garcia <berto@igalia.com>
---
hw/scsi/scsi-disk.c | 46 +++++++++++++++++++++++++++++++---------------
1 file changed, 31 insertions(+), 15 deletions(-)
diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c
index bada9a7..20a31a7 100644
--- a/hw/scsi/scsi-disk.c
+++ b/hw/scsi/scsi-disk.c
@@ -90,7 +90,7 @@ struct SCSIDiskState
bool tray_locked;
};
-static int scsi_handle_rw_error(SCSIDiskReq *r, int error);
+static int scsi_handle_rw_error(SCSIDiskReq *r, int error, bool acct_failed);
static void scsi_free_request(SCSIRequest *req)
{
@@ -169,18 +169,18 @@ static void scsi_aio_complete(void *opaque, int ret)
assert(r->req.aiocb != NULL);
r->req.aiocb = NULL;
- block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
if (r->req.io_canceled) {
scsi_req_cancel_complete(&r->req);
goto done;
}
if (ret < 0) {
- if (scsi_handle_rw_error(r, -ret)) {
+ if (scsi_handle_rw_error(r, -ret, true)) {
goto done;
}
}
+ block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
scsi_req_complete(&r->req, GOOD);
done:
@@ -247,7 +247,7 @@ static void scsi_dma_complete_noio(SCSIDiskReq *r, int ret)
}
if (ret < 0) {
- if (scsi_handle_rw_error(r, -ret)) {
+ if (scsi_handle_rw_error(r, -ret, false)) {
goto done;
}
}
@@ -273,7 +273,11 @@ static void scsi_dma_complete(void *opaque, int ret)
assert(r->req.aiocb != NULL);
r->req.aiocb = NULL;
- block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
+ if (ret < 0) {
+ block_acct_failed(blk_get_stats(s->qdev.conf.blk), &r->acct);
+ } else {
+ block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
+ }
scsi_dma_complete_noio(r, ret);
}
@@ -285,18 +289,18 @@ static void scsi_read_complete(void * opaque, int ret)
assert(r->req.aiocb != NULL);
r->req.aiocb = NULL;
- block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
if (r->req.io_canceled) {
scsi_req_cancel_complete(&r->req);
goto done;
}
if (ret < 0) {
- if (scsi_handle_rw_error(r, -ret)) {
+ if (scsi_handle_rw_error(r, -ret, true)) {
goto done;
}
}
+ block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
DPRINTF("Data ready tag=0x%x len=%zd\n", r->req.tag, r->qiov.size);
n = r->qiov.size / 512;
@@ -322,7 +326,7 @@ static void scsi_do_read(SCSIDiskReq *r, int ret)
}
if (ret < 0) {
- if (scsi_handle_rw_error(r, -ret)) {
+ if (scsi_handle_rw_error(r, -ret, false)) {
goto done;
}
}
@@ -355,7 +359,11 @@ static void scsi_do_read_cb(void *opaque, int ret)
assert (r->req.aiocb != NULL);
r->req.aiocb = NULL;
- block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
+ if (ret < 0) {
+ block_acct_failed(blk_get_stats(s->qdev.conf.blk), &r->acct);
+ } else {
+ block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
+ }
scsi_do_read(opaque, ret);
}
@@ -407,7 +415,7 @@ static void scsi_read_data(SCSIRequest *req)
* scsi_handle_rw_error always manages its reference counts, independent
* of the return value.
*/
-static int scsi_handle_rw_error(SCSIDiskReq *r, int error)
+static int scsi_handle_rw_error(SCSIDiskReq *r, int error, bool acct_failed)
{
bool is_read = (r->req.cmd.mode == SCSI_XFER_FROM_DEV);
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
@@ -415,6 +423,9 @@ static int scsi_handle_rw_error(SCSIDiskReq *r, int error)
is_read, error);
if (action == BLOCK_ERROR_ACTION_REPORT) {
+ if (acct_failed) {
+ block_acct_failed(blk_get_stats(s->qdev.conf.blk), &r->acct);
+ }
switch (error) {
case ENOMEDIUM:
scsi_check_condition(r, SENSE_CODE(NO_MEDIUM));
@@ -452,7 +463,7 @@ static void scsi_write_complete_noio(SCSIDiskReq *r, int ret)
}
if (ret < 0) {
- if (scsi_handle_rw_error(r, -ret)) {
+ if (scsi_handle_rw_error(r, -ret, false)) {
goto done;
}
}
@@ -481,7 +492,11 @@ static void scsi_write_complete(void * opaque, int ret)
assert (r->req.aiocb != NULL);
r->req.aiocb = NULL;
- block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
+ if (ret < 0) {
+ block_acct_failed(blk_get_stats(s->qdev.conf.blk), &r->acct);
+ } else {
+ block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
+ }
scsi_write_complete_noio(r, ret);
}
@@ -1592,7 +1607,7 @@ static void scsi_unmap_complete_noio(UnmapCBData *data, int ret)
}
if (ret < 0) {
- if (scsi_handle_rw_error(r, -ret)) {
+ if (scsi_handle_rw_error(r, -ret, false)) {
goto done;
}
}
@@ -1696,18 +1711,19 @@ static void scsi_write_same_complete(void *opaque, int ret)
assert(r->req.aiocb != NULL);
r->req.aiocb = NULL;
- block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
if (r->req.io_canceled) {
scsi_req_cancel_complete(&r->req);
goto done;
}
if (ret < 0) {
- if (scsi_handle_rw_error(r, -ret)) {
+ if (scsi_handle_rw_error(r, -ret, true)) {
goto done;
}
}
+ block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
+
data->nb_sectors -= data->iov.iov_len / 512;
data->sector += data->iov.iov_len / 512;
data->iov.iov_len = MIN(data->nb_sectors * 512, data->iov.iov_len);
--
2.6.1
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [Qemu-devel] [PATCH v4 21/21] block: Update copyright of the accounting code
2015-10-28 15:32 [Qemu-devel] [PATCH v4 00/21] Extended I/O accounting Alberto Garcia
` (19 preceding siblings ...)
2015-10-28 15:33 ` [Qemu-devel] [PATCH v4 20/21] scsi-disk: " Alberto Garcia
@ 2015-10-28 15:33 ` Alberto Garcia
2015-11-10 15:07 ` [Qemu-devel] [PATCH v4 00/21] Extended I/O accounting Stefan Hajnoczi
21 siblings, 0 replies; 24+ messages in thread
From: Alberto Garcia @ 2015-10-28 15:33 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Alberto Garcia, qemu-block, Markus Armbruster,
Max Reitz, Stefan Hajnoczi
Signed-off-by: Alberto Garcia <berto@igalia.com>
---
block/accounting.c | 1 +
include/block/accounting.h | 1 +
2 files changed, 2 insertions(+)
diff --git a/block/accounting.c b/block/accounting.c
index 05a5c5f..185025e 100644
--- a/block/accounting.c
+++ b/block/accounting.c
@@ -2,6 +2,7 @@
* QEMU System Emulator block accounting
*
* Copyright (c) 2011 Christoph Hellwig
+ * Copyright (c) 2015 Igalia, S.L.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
diff --git a/include/block/accounting.h b/include/block/accounting.h
index 482926b..0f46cb4 100644
--- a/include/block/accounting.h
+++ b/include/block/accounting.h
@@ -2,6 +2,7 @@
* QEMU System Emulator block accounting
*
* Copyright (c) 2011 Christoph Hellwig
+ * Copyright (c) 2015 Igalia, S.L.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
--
2.6.1
^ permalink raw reply related [flat|nested] 24+ messages in thread
* Re: [Qemu-devel] [PATCH v4 00/21] Extended I/O accounting
2015-10-28 15:32 [Qemu-devel] [PATCH v4 00/21] Extended I/O accounting Alberto Garcia
` (20 preceding siblings ...)
2015-10-28 15:33 ` [Qemu-devel] [PATCH v4 21/21] block: Update copyright of the accounting code Alberto Garcia
@ 2015-11-10 15:07 ` Stefan Hajnoczi
21 siblings, 0 replies; 24+ messages in thread
From: Stefan Hajnoczi @ 2015-11-10 15:07 UTC (permalink / raw)
To: Alberto Garcia
Cc: Kevin Wolf, qemu-block, Markus Armbruster, qemu-devel, Max Reitz
[-- Attachment #1: Type: text/plain, Size: 5845 bytes --]
On Wed, Oct 28, 2015 at 05:32:57PM +0200, Alberto Garcia wrote:
> Here's v4 of the series that implements extended I/O accounting for
> block devices.
>
> Since part of Max's BlockBackend series has already been merged, this
> series can now be applied cleanly on top of the master branch without
> additional dependencies.
>
> Here's the summary of what this series provides:
>
> - New block_acct_failed() and block_acct_invalid() calls.
> We keep track now of the number of successful, failed and invalid
> operations (each one separated into read, write and flush). So from
> the API point of view, BlockDeviceStats contains 6 new fields for
> those.
>
> - idle_time_ns: time since the last I/O operation.
>
> - New BlockDeviceTimedStats struct: it has statistics for the I/O
> during a given interval of time. It keeps minimum, maximum and
> average latencies for read, write and flush operations.
>
> It also keeps the average read and write queue depths.
>
> - New 'stats-intervals' option that allows the user to define the
> intervals used to keep the aforementioned statistics. An arbitrary
> number of intervals can be specified, the length of each one is in
> seconds.
>
> For the API I opted for a colon-separated list of numbers,
>
> stats-intervals=60:3600:86400
>
> I also considered something a different syntax,
>
> stats-intervals.0.length=60,
> stats-intervals.1.length=3600,
> stats-intervals.2.length=86400
>
> This one could be useful if we want to specify any other attribute
> for each interval, but I couldn't come up with any, so I chose the
> simpler solution.
>
> - Two new options, stats-account-invalid and stats-account-failed,
> which allow the user to decide whether to count invalid and failed
> operations when computing the idle time and total latency.
>
> Regards,
>
> Berto
>
> v4:
> - Rebase on top of the current master. This series no longer depends
> on any other.
> - patch 8: clarify that interval_length is in seconds [Stefan]
> - patch 9: rewrite timed_average_sum() so it does not call
> qemu_clock_get_ns() twice [Stefan]
>
> v3: https://lists.gnu.org/archive/html/qemu-block/2015-10/msg00785.html
> - Rebased on top of the current master and on Max's BlockBackend
> series v7
> - patch 4: minor documentation fixes [Stefan]
> - patch 5: s/miliseconds/nanoseconds/ [Stefan]
> - patch 6: dropped, there's no "supports_stats" anymore [Stefan]
> - patch 7 (now 6): explain why block_acct_invalid() does not update
> total_time_ns[] [Stefan]
> - patch 12 (now 11): don't initialize BlockAcctCookie to { 0 }, it's
> not needed.
>
> v2: https://lists.gnu.org/archive/html/qemu-block/2015-10/msg00161.html
> - First complete implementation of the new statistics
>
> v1: https://lists.gnu.org/archive/html/qemu-devel/2015-06/msg03321.html
> - Initial series containing only the timed average infrastructure.
>
> Alberto Garcia (21):
> xen_disk: Account for flush operations
> ide: Account for write operations correctly
> block: define 'clock_type' for the accounting code
> util: Infrastructure for computing recent averages
> block: Add idle_time_ns to BlockDeviceStats
> block: Add statistics for failed and invalid I/O operations
> block: Allow configuring whether to account failed and invalid ops
> block: Compute minimum, maximum and average I/O latencies
> block: Add average I/O queue depth to BlockDeviceTimedStats
> block: New option to define the intervals for collecting I/O
> statistics
> qemu-io: Account for failed, invalid and flush operations
> block: Use QEMU_CLOCK_VIRTUAL for the accounting code in qtest mode
> iotests: Add test for the block device statistics
> nvme: Account for failed and invalid operations
> virtio-blk: Account for failed and invalid operations
> xen_disk: Account for failed and invalid operations
> atapi: Account for failed and invalid operations
> ide: Account for failed and invalid operations
> macio: Account for failed operations
> scsi-disk: Account for failed operations
> block: Update copyright of the accounting code
>
> block/accounting.c | 123 ++++++++++++++-
> block/block-backend.c | 1 +
> block/qapi.c | 51 +++++++
> blockdev.c | 53 +++++++
> hmp.c | 4 +-
> hw/block/nvme.c | 11 +-
> hw/block/virtio-blk.c | 4 +-
> hw/block/xen_disk.c | 27 +++-
> hw/ide/atapi.c | 31 ++--
> hw/ide/core.c | 12 +-
> hw/ide/macio.c | 12 +-
> hw/scsi/scsi-disk.c | 46 ++++--
> include/block/accounting.h | 28 ++++
> include/qemu/timed-average.h | 64 ++++++++
> qapi/block-core.json | 103 ++++++++++++-
> qemu-io-cmds.c | 9 ++
> qmp-commands.hx | 80 +++++++++-
> tests/Makefile | 4 +
> tests/qemu-iotests/136 | 349 +++++++++++++++++++++++++++++++++++++++++++
> tests/qemu-iotests/136.out | 5 +
> tests/qemu-iotests/group | 1 +
> tests/test-timed-average.c | 90 +++++++++++
> util/Makefile.objs | 1 +
> util/timed-average.c | 231 ++++++++++++++++++++++++++++
> 24 files changed, 1292 insertions(+), 48 deletions(-)
> create mode 100644 include/qemu/timed-average.h
> create mode 100644 tests/qemu-iotests/136
> create mode 100644 tests/qemu-iotests/136.out
> create mode 100644 tests/test-timed-average.c
> create mode 100644 util/timed-average.c
>
> --
> 2.6.1
>
Thanks, applied to my block tree:
https://github.com/stefanha/qemu/commits/block
Stefan
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 473 bytes --]
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [Qemu-devel] [PATCH v4 10/21] block: New option to define the intervals for collecting I/O statistics
2015-10-28 15:33 ` [Qemu-devel] [PATCH v4 10/21] block: New option to define the intervals for collecting I/O statistics Alberto Garcia
@ 2015-11-10 17:27 ` Eric Blake
0 siblings, 0 replies; 24+ messages in thread
From: Eric Blake @ 2015-11-10 17:27 UTC (permalink / raw)
To: Alberto Garcia, qemu-devel
Cc: Kevin Wolf, Markus Armbruster, Stefan Hajnoczi, qemu-block,
Max Reitz
[-- Attachment #1: Type: text/plain, Size: 1652 bytes --]
On 10/28/2015 09:33 AM, Alberto Garcia wrote:
> The BlockAcctStats structure contains a list of BlockAcctTimedStats.
> Each one of these collects statistics about the minimum, maximum and
> average latencies of all I/O operations in a certain interval of time.
>
> This patch adds a new "stats-intervals" option that allows defining
> these intervals.
>
> Signed-off-by: Alberto Garcia <berto@igalia.com>
> ---
> blockdev.c | 37 +++++++++++++++++++++++++++++++++++++
> qapi/block-core.json | 4 ++++
> 2 files changed, 41 insertions(+)
> +++ b/qapi/block-core.json
> @@ -1503,6 +1503,9 @@
> # @stats-account-failed: #optional whether to include failed
> # operations when computing latency and last
> # access statistics (default: true) (Since 2.5)
> +# @stats-intervals: #optional colon-separated list of intervals for
> +# collecting I/O statistics, in seconds (default: none)
> +# (Since 2.5)
> # @detect-zeroes: #optional detect and optimize zero writes (Since 2.1)
> # (default: off)
> #
> @@ -1520,6 +1523,7 @@
> '*read-only': 'bool',
> '*stats-account-invalid': 'bool',
> '*stats-account-failed': 'bool',
> + '*stats-intervals': 'str',
My fault for not reviewing this change to a .json file prior to the PULL
request, but I think this should be '*stats-intervals':['int'] so that
we aren't post-processing to parse out colons.
--
Eric Blake eblake redhat com +1-919-301-3266
Libvirt virtualization library http://libvirt.org
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]
^ permalink raw reply [flat|nested] 24+ messages in thread
end of thread, other threads:[~2015-11-10 17:27 UTC | newest]
Thread overview: 24+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-10-28 15:32 [Qemu-devel] [PATCH v4 00/21] Extended I/O accounting Alberto Garcia
2015-10-28 15:32 ` [Qemu-devel] [PATCH v4 01/21] xen_disk: Account for flush operations Alberto Garcia
2015-10-28 15:32 ` [Qemu-devel] [PATCH v4 02/21] ide: Account for write operations correctly Alberto Garcia
2015-10-28 15:33 ` [Qemu-devel] [PATCH v4 03/21] block: define 'clock_type' for the accounting code Alberto Garcia
2015-10-28 15:33 ` [Qemu-devel] [PATCH v4 04/21] util: Infrastructure for computing recent averages Alberto Garcia
2015-10-28 15:33 ` [Qemu-devel] [PATCH v4 05/21] block: Add idle_time_ns to BlockDeviceStats Alberto Garcia
2015-10-28 15:33 ` [Qemu-devel] [PATCH v4 06/21] block: Add statistics for failed and invalid I/O operations Alberto Garcia
2015-10-28 15:33 ` [Qemu-devel] [PATCH v4 07/21] block: Allow configuring whether to account failed and invalid ops Alberto Garcia
2015-10-28 15:33 ` [Qemu-devel] [PATCH v4 08/21] block: Compute minimum, maximum and average I/O latencies Alberto Garcia
2015-10-28 15:33 ` [Qemu-devel] [PATCH v4 09/21] block: Add average I/O queue depth to BlockDeviceTimedStats Alberto Garcia
2015-10-28 15:33 ` [Qemu-devel] [PATCH v4 10/21] block: New option to define the intervals for collecting I/O statistics Alberto Garcia
2015-11-10 17:27 ` Eric Blake
2015-10-28 15:33 ` [Qemu-devel] [PATCH v4 11/21] qemu-io: Account for failed, invalid and flush operations Alberto Garcia
2015-10-28 15:33 ` [Qemu-devel] [PATCH v4 12/21] block: Use QEMU_CLOCK_VIRTUAL for the accounting code in qtest mode Alberto Garcia
2015-10-28 15:33 ` [Qemu-devel] [PATCH v4 13/21] iotests: Add test for the block device statistics Alberto Garcia
2015-10-28 15:33 ` [Qemu-devel] [PATCH v4 14/21] nvme: Account for failed and invalid operations Alberto Garcia
2015-10-28 15:33 ` [Qemu-devel] [PATCH v4 15/21] virtio-blk: " Alberto Garcia
2015-10-28 15:33 ` [Qemu-devel] [PATCH v4 16/21] xen_disk: " Alberto Garcia
2015-10-28 15:33 ` [Qemu-devel] [PATCH v4 17/21] atapi: " Alberto Garcia
2015-10-28 15:33 ` [Qemu-devel] [PATCH v4 18/21] ide: " Alberto Garcia
2015-10-28 15:33 ` [Qemu-devel] [PATCH v4 19/21] macio: Account for failed operations Alberto Garcia
2015-10-28 15:33 ` [Qemu-devel] [PATCH v4 20/21] scsi-disk: " Alberto Garcia
2015-10-28 15:33 ` [Qemu-devel] [PATCH v4 21/21] block: Update copyright of the accounting code Alberto Garcia
2015-11-10 15:07 ` [Qemu-devel] [PATCH v4 00/21] Extended I/O accounting Stefan Hajnoczi
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).