From nobody Thu May 2 02:53:12 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=virtuozzo.com Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 152061145515453.03393474554855; Fri, 9 Mar 2018 08:04:15 -0800 (PST) Received: from localhost ([::1]:46159 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1euKV6-0007Fc-CA for importer@patchew.org; Fri, 09 Mar 2018 11:04:08 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:56646) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1euKTZ-0006Yt-Lc for qemu-devel@nongnu.org; Fri, 09 Mar 2018 11:02:40 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1euKTU-0005yp-QP for qemu-devel@nongnu.org; Fri, 09 Mar 2018 11:02:33 -0500 Received: from relay.sw.ru ([185.231.240.75]:40034) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1euKTU-0005xx-IH; Fri, 09 Mar 2018 11:02:28 -0500 Received: from msk-vpn.virtuozzo.com ([195.214.232.6] helo=kvm.sw.ru) by relay.sw.ru with esmtp (Exim 4.89) (envelope-from ) id 1euKTR-0006SH-Dv; Fri, 09 Mar 2018 19:02:25 +0300 From: Vladimir Sementsov-Ogievskiy To: qemu-devel@nongnu.org, qemu-block@nongnu.org Date: Fri, 9 Mar 2018 19:02:23 +0300 Message-Id: <20180309160224.78821-2-vsementsov@virtuozzo.com> X-Mailer: git-send-email 2.11.1 In-Reply-To: <20180309160224.78821-1-vsementsov@virtuozzo.com> References: <20180309160224.78821-1-vsementsov@virtuozzo.com> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x [fuzzy] X-Received-From: 185.231.240.75 Subject: [Qemu-devel] [PATCH v5 1/2] block/accounting: introduce latency histogram X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: kwolf@redhat.com, vsementsov@virtuozzo.com, armbru@redhat.com, mreitz@redhat.com, nshirokovskiy@virtuozzo.com, den@openvz.org Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Introduce latency histogram statics for block devices. For each accounted operation type latency region [0, +inf) is divided into subregions by several points. Then, calculate hits for each subregion. Signed-off-by: Vladimir Sementsov-Ogievskiy --- include/block/accounting.h | 35 ++++++++++++++++++ block/accounting.c | 91 ++++++++++++++++++++++++++++++++++++++++++= ++++ 2 files changed, 126 insertions(+) diff --git a/include/block/accounting.h b/include/block/accounting.h index b833d26d6c..6eb2dd9ed3 100644 --- a/include/block/accounting.h +++ b/include/block/accounting.h @@ -27,6 +27,7 @@ =20 #include "qemu/timed-average.h" #include "qemu/thread.h" +#include "qapi-types.h" =20 typedef struct BlockAcctTimedStats BlockAcctTimedStats; typedef struct BlockAcctStats BlockAcctStats; @@ -45,6 +46,36 @@ struct BlockAcctTimedStats { QSLIST_ENTRY(BlockAcctTimedStats) entries; }; =20 +typedef struct BlockLatencyHistogram { + /* The following histogram is represented like this: + * + * 5| * + * 4| * + * 3| * * + * 2| * * * + * 1| * * * * + * +------------------ + * 10 50 100 + * + * BlockLatencyHistogram histogram =3D { + * .nbins =3D 4, + * .boundaries =3D {10, 50, 100}, + * .bins =3D {3, 1, 5, 2}, + * }; + * + * @boundaries array define histogram intervals as follows: + * [0, boundaries[0]), [boundaries[0], boundaries[1]), ... + * [boundaries[nbins-2], +inf) + * + * So, for example above, histogram intervals are: + * [0, 10), [10, 50), [50, 100), [100, +inf) + */ + int nbins; + uint64_t *boundaries; /* @nbins-1 numbers here + (all boundaries, except 0 and +inf) */ + uint64_t *bins; +} BlockLatencyHistogram; + struct BlockAcctStats { QemuMutex lock; uint64_t nr_bytes[BLOCK_MAX_IOTYPE]; @@ -57,6 +88,7 @@ struct BlockAcctStats { QSLIST_HEAD(, BlockAcctTimedStats) intervals; bool account_invalid; bool account_failed; + BlockLatencyHistogram latency_histogram[BLOCK_MAX_IOTYPE]; }; =20 typedef struct BlockAcctCookie { @@ -82,5 +114,8 @@ void block_acct_merge_done(BlockAcctStats *stats, enum B= lockAcctType type, int64_t block_acct_idle_time_ns(BlockAcctStats *stats); double block_acct_queue_depth(BlockAcctTimedStats *stats, enum BlockAcctType type); +int block_latency_histogram_set(BlockAcctStats *stats, enum BlockAcctType = type, + uint64List *boundaries); +void block_latency_histograms_clear(BlockAcctStats *stats); =20 #endif diff --git a/block/accounting.c b/block/accounting.c index 87ef5bbfaa..70a3d9a426 100644 --- a/block/accounting.c +++ b/block/accounting.c @@ -94,6 +94,94 @@ void block_acct_start(BlockAcctStats *stats, BlockAcctCo= okie *cookie, cookie->type =3D type; } =20 +/* block_latency_histogram_compare_func: + * Compare @key with interval [@it[0], @it[1]). + * Return: -1 if @key < @it[0] + * 0 if @key in [@it[0], @it[1]) + * +1 if @key >=3D @it[1] + */ +static int block_latency_histogram_compare_func(const void *key, const voi= d *it) +{ + uint64_t k =3D *(uint64_t *)key; + uint64_t a =3D ((uint64_t *)it)[0]; + uint64_t b =3D ((uint64_t *)it)[1]; + + return k < a ? -1 : (k < b ? 0 : 1); +} + +static void block_latency_histogram_account(BlockLatencyHistogram *hist, + int64_t latency_ns) +{ + uint64_t *pos; + + if (hist->bins =3D=3D NULL) { + /* histogram disabled */ + return; + } + + + if (latency_ns < hist->boundaries[0]) { + hist->bins[0]++; + return; + } + + if (latency_ns >=3D hist->boundaries[hist->nbins - 2]) { + hist->bins[hist->nbins - 1]++; + return; + } + + pos =3D bsearch(&latency_ns, hist->boundaries, hist->nbins - 2, + sizeof(hist->boundaries[0]), + block_latency_histogram_compare_func); + assert(pos !=3D NULL); + + hist->bins[pos - hist->boundaries + 1]++; +} + +int block_latency_histogram_set(BlockAcctStats *stats, enum BlockAcctType = type, + uint64List *boundaries) +{ + BlockLatencyHistogram *hist =3D &stats->latency_histogram[type]; + uint64List *entry; + uint64_t *ptr; + uint64_t prev =3D 0; + int new_nbins =3D 1; + + for (entry =3D boundaries; entry; entry =3D entry->next) { + if (entry->value <=3D prev) { + return -EINVAL; + } + new_nbins++; + prev =3D entry->value; + } + + hist->nbins =3D new_nbins; + g_free(hist->boundaries); + hist->boundaries =3D g_new(uint64_t, hist->nbins - 1); + for (entry =3D boundaries, ptr =3D hist->boundaries; entry; + entry =3D entry->next, ptr++) + { + *ptr =3D entry->value; + } + + g_free(hist->bins); + hist->bins =3D g_new0(uint64_t, hist->nbins); + + return 0; +} + +void block_latency_histograms_clear(BlockAcctStats *stats) +{ + int i; + + for (i =3D 0; i < BLOCK_MAX_IOTYPE; i++) { + BlockLatencyHistogram *hist =3D &stats->latency_histogram[i]; + g_free(hist->bins); + g_free(hist->boundaries); + memset(hist, 0, sizeof(*hist)); + } +} + static void block_account_one_io(BlockAcctStats *stats, BlockAcctCookie *c= ookie, bool failed) { @@ -116,6 +204,9 @@ static void block_account_one_io(BlockAcctStats *stats,= BlockAcctCookie *cookie, stats->nr_ops[cookie->type]++; } =20 + block_latency_histogram_account(&stats->latency_histogram[cookie->type= ], + latency_ns); + if (!failed || stats->account_failed) { stats->total_time_ns[cookie->type] +=3D latency_ns; stats->last_access_time_ns =3D time_ns; --=20 2.11.1 From nobody Thu May 2 02:53:12 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=virtuozzo.com Return-Path: Received: from lists.gnu.org (208.118.235.17 [208.118.235.17]) by mx.zohomail.com with SMTPS id 1520611456840770.7863597325423; Fri, 9 Mar 2018 08:04:16 -0800 (PST) Received: from localhost ([::1]:46158 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1euKV1-0007Bl-PE for importer@patchew.org; Fri, 09 Mar 2018 11:04:03 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:56648) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1euKTZ-0006Yv-Lu for qemu-devel@nongnu.org; Fri, 09 Mar 2018 11:02:36 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1euKTU-0005yy-UF for qemu-devel@nongnu.org; Fri, 09 Mar 2018 11:02:33 -0500 Received: from relay.sw.ru ([185.231.240.75]:40042) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1euKTU-0005xw-IL; Fri, 09 Mar 2018 11:02:28 -0500 Received: from msk-vpn.virtuozzo.com ([195.214.232.6] helo=kvm.sw.ru) by relay.sw.ru with esmtp (Exim 4.89) (envelope-from ) id 1euKTR-0006SH-Ns; Fri, 09 Mar 2018 19:02:25 +0300 From: Vladimir Sementsov-Ogievskiy To: qemu-devel@nongnu.org, qemu-block@nongnu.org Date: Fri, 9 Mar 2018 19:02:24 +0300 Message-Id: <20180309160224.78821-3-vsementsov@virtuozzo.com> X-Mailer: git-send-email 2.11.1 In-Reply-To: <20180309160224.78821-1-vsementsov@virtuozzo.com> References: <20180309160224.78821-1-vsementsov@virtuozzo.com> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x [fuzzy] X-Received-From: 185.231.240.75 Subject: [Qemu-devel] [PATCH v5 2/2] qapi: add block latency histogram interface X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: kwolf@redhat.com, vsementsov@virtuozzo.com, armbru@redhat.com, mreitz@redhat.com, nshirokovskiy@virtuozzo.com, den@openvz.org Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Set (and clear) histogram through new command block-latency-histogram-set and show new statistics in query-blockstats results. Signed-off-by: Vladimir Sementsov-Ogievskiy --- qapi/block-core.json | 111 +++++++++++++++++++++++++++++++++++++++++++++++= +++- block/qapi.c | 41 +++++++++++++++++++ blockdev.c | 43 ++++++++++++++++++++ 3 files changed, 194 insertions(+), 1 deletion(-) diff --git a/qapi/block-core.json b/qapi/block-core.json index 5c5921bfb7..b4dc9af45a 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -451,6 +451,106 @@ 'status': 'DirtyBitmapStatus'} } =20 ## +# @XBlockLatencyHistogramInfo: +# +# Block latency histogram. +# +# @boundaries: list of interval boundary values in nanoseconds, all greater +# than zero and in ascending order. +# For example, the list [10, 50, 100] produces the following +# histogram intervals: [0, 10), [10, 50), [50, 100), [100, +i= nf). +# +# @bins: list of io request counts corresponding to histogram intervals. +# len(@bins) =3D len(@boundaries) + 1 +# For the example above, @bins may be something like [3, 1, 5, 2], +# and corresponding histogram looks like: +# +# 5| * +# 4| * +# 3| * * +# 2| * * * +# 1| * * * * +# +------------------ +# 10 50 100 +# +# Since: 2.12 +## +{ 'struct': 'XBlockLatencyHistogramInfo', + 'data': {'boundaries': ['uint64'], 'bins': ['uint64'] } } + +## +# @x-block-latency-histogram-set: +# +# Manage read, write and flush latency histograms for the device. +# +# If only @device parameter is specified, remove all present latency histo= grams +# for the device. Otherwise, add/reset some of (or all) latency histograms. +# +# @device: device name to set latency histogram for. +# +# @boundaries: list of interval boundary values (see description in +# XBlockLatencyHistogramInfo definition). If specified, all +# latency histograms are removed, and empty ones created for = all +# io types with intervals corresponding to @boundaries (excep= t for +# io types, for which specific boundaries are set through the +# following parameters). +# +# @boundaries-read: list of interval boundary values for read latency +# histogram. If specified, old read latency histogram is +# removed, and empty one created with interavals +# corresponding to @boundaries-read. The parameter has h= igher +# priority then @boundaries. +# +# @boundaries-write: list of interaval boundary values for write latency +# histogram. +# +# @boundaries-flush: list of interaval boundary values for flush latency +# histogram. +# +# Returns: error if device is not found or @boundaries* arrays are invalid. +# +# Since: 2.12 +# +# Example: set new histograms for all io types with intervals +# [0, 10), [10, 50), [50, 100), [100, +inf): +# +# -> { "execute": "block-latency-histogram-set", +# "arguments": { "device": "drive0", +# "boundaries": [10, 50, 100] } } +# <- { "return": {} } +# +# Example: set new histogram only for write, other histograms will remain +# not changed (or not created): +# +# -> { "execute": "block-latency-histogram-set", +# "arguments": { "device": "drive0", +# "boundaries-write": [10, 50, 100] } } +# <- { "return": {} } +# +# Example: set new histograms with the following intervals: +# read, flush: [0, 10), [10, 50), [50, 100), [100, +inf) +# write: [0, 1000), [1000, 5000), [5000, +inf) +# +# -> { "execute": "block-latency-histogram-set", +# "arguments": { "device": "drive0", +# "boundaries": [10, 50, 100], +# "boundaries-write": [1000, 5000] } } +# <- { "return": {} } +# +# Example: remove all latency histograms: +# +# -> { "execute": "block-latency-histogram-set", +# "arguments": { "device": "drive0" } } +# <- { "return": {} } +## +{ 'command': 'x-block-latency-histogram-set', + 'data': {'device': 'str', + '*boundaries': ['uint64'], + '*boundaries-read': ['uint64'], + '*boundaries-write': ['uint64'], + '*boundaries-flush': ['uint64'] } } + +## # @BlockInfo: # # Block device information. This structure describes a virtual device and @@ -730,6 +830,12 @@ # @timed_stats: Statistics specific to the set of previously defined # intervals of time (Since 2.5) # +# @x_rd_latency_histogram: @BlockLatencyHistogramInfo. (Since 2.12) +# +# @x_wr_latency_histogram: @BlockLatencyHistogramInfo. (Since 2.12) +# +# @x_flush_latency_histogram: @BlockLatencyHistogramInfo. (Since 2.12) +# # Since: 0.14.0 ## { 'struct': 'BlockDeviceStats', @@ -742,7 +848,10 @@ 'failed_flush_operations': 'int', 'invalid_rd_operations': 'int= ', 'invalid_wr_operations': 'int', 'invalid_flush_operations': 'in= t', 'account_invalid': 'bool', 'account_failed': 'bool', - 'timed_stats': ['BlockDeviceTimedStats'] } } + 'timed_stats': ['BlockDeviceTimedStats'], + '*x_rd_latency_histogram': 'XBlockLatencyHistogramInfo', + '*x_wr_latency_histogram': 'XBlockLatencyHistogramInfo', + '*x_flush_latency_histogram': 'XBlockLatencyHistogramInfo' } } =20 ## # @BlockStats: diff --git a/block/qapi.c b/block/qapi.c index 1fdeb1ef2f..32bd0d45c4 100644 --- a/block/qapi.c +++ b/block/qapi.c @@ -394,6 +394,37 @@ static void bdrv_query_info(BlockBackend *blk, BlockIn= fo **p_info, qapi_free_BlockInfo(info); } =20 +static uint64List *uint64_list(uint64_t *list, int size) +{ + int i; + uint64List *out_list =3D NULL; + uint64List **pout_list =3D &out_list; + + for (i =3D 0; i < size; i++) { + uint64List *entry =3D g_new(uint64List, 1); + entry->value =3D list[i]; + *pout_list =3D entry; + pout_list =3D &entry->next; + } + + *pout_list =3D NULL; + + return out_list; +} + +static void bdrv_latency_histogram_stats(BlockLatencyHistogram *hist, + bool *not_null, + XBlockLatencyHistogramInfo **info) +{ + *not_null =3D hist->bins !=3D NULL; + if (*not_null) { + *info =3D g_new0(XBlockLatencyHistogramInfo, 1); + + (*info)->boundaries =3D uint64_list(hist->boundaries, hist->nbins = - 1); + (*info)->bins =3D uint64_list(hist->bins, hist->nbins); + } +} + static void bdrv_query_blk_stats(BlockDeviceStats *ds, BlockBackend *blk) { BlockAcctStats *stats =3D blk_get_stats(blk); @@ -459,6 +490,16 @@ static void bdrv_query_blk_stats(BlockDeviceStats *ds,= BlockBackend *blk) dev_stats->avg_wr_queue_depth =3D block_acct_queue_depth(ts, BLOCK_ACCT_WRITE); } + + bdrv_latency_histogram_stats(&stats->latency_histogram[BLOCK_ACCT_READ= ], + &ds->has_x_rd_latency_histogram, + &ds->x_rd_latency_histogram); + bdrv_latency_histogram_stats(&stats->latency_histogram[BLOCK_ACCT_WRIT= E], + &ds->has_x_wr_latency_histogram, + &ds->x_wr_latency_histogram); + bdrv_latency_histogram_stats(&stats->latency_histogram[BLOCK_ACCT_FLUS= H], + &ds->has_x_flush_latency_histogram, + &ds->x_flush_latency_histogram); } =20 static BlockStats *bdrv_query_bds_stats(BlockDriverState *bs, diff --git a/blockdev.c b/blockdev.c index 3fb1ca803c..8842d0aea2 100644 --- a/blockdev.c +++ b/blockdev.c @@ -4179,6 +4179,49 @@ void qmp_x_blockdev_set_iothread(const char *node_na= me, StrOrNull *iothread, aio_context_release(old_context); } =20 +void qmp_x_block_latency_histogram_set( + const char *device, + bool has_boundaries, uint64List *boundaries, + bool has_boundaries_read, uint64List *boundaries_read, + bool has_boundaries_write, uint64List *boundaries_write, + bool has_boundaries_flush, uint64List *boundaries_flush, + Error **errp) +{ + BlockBackend *blk =3D blk_by_name(device); + BlockAcctStats *stats; + + if (!blk) { + error_setg(errp, "Device '%s' not found", device); + return; + } + stats =3D blk_get_stats(blk); + + if (!has_boundaries && !has_boundaries_read && !has_boundaries_write && + !has_boundaries_flush) + { + block_latency_histograms_clear(stats); + return; + } + + if (has_boundaries || has_boundaries_read) { + block_latency_histogram_set( + stats, BLOCK_ACCT_READ, + has_boundaries_read ? boundaries_read : boundaries); + } + + if (has_boundaries || has_boundaries_write) { + block_latency_histogram_set( + stats, BLOCK_ACCT_WRITE, + has_boundaries_write ? boundaries_write : boundaries); + } + + if (has_boundaries || has_boundaries_flush) { + block_latency_histogram_set( + stats, BLOCK_ACCT_FLUSH, + has_boundaries_flush ? boundaries_flush : boundaries); + } +} + QemuOptsList qemu_common_drive_opts =3D { .name =3D "drive", .head =3D QTAILQ_HEAD_INITIALIZER(qemu_common_drive_opts.head), --=20 2.11.1