From nobody Mon Feb 9 06:02:13 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.libvirt.org designates 8.43.85.245 as permitted sender) client-ip=8.43.85.245; envelope-from=devel-bounces@lists.libvirt.org; helo=lists.libvirt.org; Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of lists.libvirt.org designates 8.43.85.245 as permitted sender) smtp.mailfrom=devel-bounces@lists.libvirt.org; dmarc=pass(p=reject dis=none) header.from=lists.libvirt.org ARC-Seal: i=1; a=rsa-sha256; t=1770045332; cv=none; d=zohomail.com; s=zohoarc; b=XlPPhoAok19yUJZ85dG6ES/joW/wB599BzfLHkN3YO6ZCoz17xXm2Fhh5D4B9q5qLWUnQEYv5nde08RI/eTeQfg4bW7ZzfS8GkwTpI/kYJ93IZgoLRq6htwh5a3C7INcgCSZx4l4zT+Ga/QbEOf+MNlU83PTp8vuCOogUvqt46U= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1770045332; h=Content-Type:Content-Transfer-Encoding:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Owner:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:Reply-To:Reply-To:References:Subject:Subject:To:To:Message-Id:Cc; bh=QadH9ErMG0XLDFJzHm6CPA8COo1VCcireRX4tJR2yFI=; b=mkRVqmau7PCyGfQR9noV5hfffu4NRmK5xf94Rm9tonBMUFu7h0xvAHwLg6GX5fCAWx9rX7Ov/dWvtM2T6qO8ezWGQnAO5+q31sQBLoSeAAt5p29qeYTvJdRLtqtJLHeK7zYu40Drfzec3uPIolBIPjRjolG9vrXQAOXBJRHEGK0= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of lists.libvirt.org designates 8.43.85.245 as permitted sender) smtp.mailfrom=devel-bounces@lists.libvirt.org; dmarc=pass header.from= (p=reject dis=none) Return-Path: Received: from lists.libvirt.org (lists.libvirt.org [8.43.85.245]) by mx.zohomail.com with SMTPS id 1770045332701895.070799113225; Mon, 2 Feb 2026 07:15:32 -0800 (PST) Received: by lists.libvirt.org (Postfix, from userid 993) id CB7B243FC7; Mon, 2 Feb 2026 10:15:31 -0500 (EST) Received: from [172.19.199.6] (lists.libvirt.org [8.43.85.245]) by lists.libvirt.org (Postfix) with ESMTP id 9193C44108; Mon, 2 Feb 2026 10:08:35 -0500 (EST) Received: by lists.libvirt.org (Postfix, from userid 993) id 741A441B4F; Mon, 2 Feb 2026 10:08:29 -0500 (EST) Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (3072 bits) server-digest SHA256) (No client certificate requested) by lists.libvirt.org (Postfix) with ESMTPS id 8ED0843E61 for ; Mon, 2 Feb 2026 10:07:00 -0500 (EST) Received: from mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-389-wEIQ3j1dNtSXJn1eqCI4CQ-1; Mon, 02 Feb 2026 10:06:58 -0500 Received: from mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.111]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 1AB7B19560B4 for ; Mon, 2 Feb 2026 15:06:58 +0000 (UTC) Received: from speedmetal.lan (unknown [10.45.242.3]) by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 647CE18002A6 for ; Mon, 2 Feb 2026 15:06:57 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-26) on lists.libvirt.org X-Spam-Level: X-Spam-Status: No, score=-5.0 required=5.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_DNSWL_MED, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED,RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED,SPF_PASS autolearn=unavailable autolearn_force=no version=4.0.1 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1770044820; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=QadH9ErMG0XLDFJzHm6CPA8COo1VCcireRX4tJR2yFI=; b=LoA3bBwdKW7hEgqQ7FGlVt1pyBktkL8v3gaNw3TQPnqQn9rU5dqbAW+KNJp5ds3zS8v0kS AwV8wg413k84lo+Y9FehVKBFXXXZhZd/oRUs4nKNoMxdUMJwLpYnSvhU8nBiEiaBwekrig jB6wzPjXd6CjL9CQUGgChAbP8P8L80o= X-MC-Unique: wEIQ3j1dNtSXJn1eqCI4CQ-1 X-Mimecast-MFC-AGG-ID: wEIQ3j1dNtSXJn1eqCI4CQ_1770044818 To: devel@lists.libvirt.org Subject: [PATCH 8/9] Introduce support for disk operation latency histogram collection Date: Mon, 2 Feb 2026 16:06:45 +0100 Message-ID: <00a0c9323f02d61e15463aa69122f56b1e78b561.1770044532.git.pkrempa@redhat.com> In-Reply-To: References: MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.111 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: LKQbOIhavVJW2AxLo_koKmb8HNzRAM6oWi74g13pVKg_1770044818 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: quoted-printable Message-ID-Hash: FLJK2G7QHVLGSLRZRJTJMRDSKFYOFQ3Z X-Message-ID-Hash: FLJK2G7QHVLGSLRZRJTJMRDSKFYOFQ3Z X-MailFrom: pkrempa@redhat.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; header-match-devel.lists.libvirt.org-0; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.10 Precedence: list List-Id: Development discussions about the libvirt library & tools Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Peter Krempa via Devel Reply-To: Peter Krempa X-ZohoMail-DKIM: fail (Header signature does not verify) X-ZM-MESSAGEID: 1770045334160158500 Content-Type: text/plain; charset="utf-8" From: Peter Krempa Add config and docs allowing enabling latency histogram collection for block device operations. This patch sets up the docs, schema and XML infrastructure. Signed-off-by: Peter Krempa --- docs/formatdomain.rst | 41 ++++++ src/conf/domain_conf.c | 133 +++++++++++++++++- src/conf/domain_conf.h | 7 + src/conf/schemas/domaincommon.rng | 37 ++++- ...isk-statistics-intervals.x86_64-latest.xml | 29 ++++ .../disk-statistics-intervals.xml | 25 ++++ 6 files changed, 262 insertions(+), 10 deletions(-) diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst index 3c7dbf97db..799c648980 100644 --- a/docs/formatdomain.rst +++ b/docs/formatdomain.rst @@ -3642,6 +3642,47 @@ paravirtualized driver is specified via the ``disk``= element. :since:`Since 11.9.0 (QEMU 10.2, virtio, ide, scsi disks only)`. + Block operation latency histogram collection can be configured using + ```` sub-element. The histogram is collected for + the whole runtime of the VM, but can be re-started or reconfigured u= sing + the `virDomainUpdateDeviceFlags `__ + API. Using the same config re-starts histogram collection. + + The optional ``type`` attribute configures specific operation to col= lect + the histogram for. Supported types are ``read``, ``write``, ``zone``= , and + ``flush``. If the ``type`` attribute is omitted the histogram collec= tion + bins bins apply to all of the aforementioned types, which can be ove= rriden + with specific config. + + The ```` has multiple mandatory ```` sub-ele= ments + with mandatory ``start`` attribute configuring the starting boundary= of + the histogram bin configured in nanosecods of the operation duration= and + the intervals must be properly ordered and non-duplicate. + + Example:: + + + + + + + + + + + [or for specific operation types] + + + + + + + + + + + :since:`Since 12.1.0`. + - The optional ``queues`` attribute specifies the number of virt queue= s for virtio-blk ( :since:`Since 3.9.0` ) or vhost-user-blk ( :since:`Since 7.1.0` ) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 02e23f7866..1fc3ef966d 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -2446,6 +2446,11 @@ virDomainDiskDefFree(virDomainDiskDef *def) virObjectUnref(def->privateData); g_slist_free_full(def->iothreads, (GDestroyNotify) virDomainIothreadMa= ppingDefFree); g_free(def->statistics); + g_free(def->histogram_boundaries); + g_free(def->histogram_boundaries_read); + g_free(def->histogram_boundaries_write); + g_free(def->histogram_boundaries_zone); + g_free(def->histogram_boundaries_flush); if (def->throttlefilters) { size_t i; @@ -8311,6 +8316,91 @@ virDomainIothreadMappingDefParse(xmlNodePtr driverNo= de, } +static int +virDomainDiskDefDriverParseXMLHistogramOne(virDomainDiskDef *def, + xmlNodePtr cur) +{ + g_autofree char *histogram_type =3D NULL; + unsigned int **histogram_config =3D NULL; + g_autoptr(GPtrArray) binNodes =3D virXMLNodeGetSubelementList(cur, "bi= n"); + size_t nbins =3D 0; + size_t i; + + if ((histogram_type =3D virXMLPropString(cur, "type"))) { + if (STREQ(histogram_type, "read")) { + histogram_config =3D &def->histogram_boundaries_read; + } else if (STREQ(histogram_type, "write")) { + histogram_config =3D &def->histogram_boundaries_write; + } else if (STREQ(histogram_type, "zone")) { + histogram_config =3D &def->histogram_boundaries_zone; + } else if (STREQ(histogram_type, "flush")) { + histogram_config =3D &def->histogram_boundaries_flush; + } else { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unknown latency_histogram type '%1$s'"), + histogram_type); + return -1; + } + } else { + histogram_config =3D &def->histogram_boundaries; + } + + if (*histogram_config) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("only one latency-histogram of a given type is su= pported")); + return -1; + } + + if (binNodes->len =3D=3D 0) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("missing 'bin' elements for 'latency-histogram'")= ); + return -1; + } + + *histogram_config =3D g_new0(unsigned int, binNodes->len + 1); + + for (i =3D 0; i < binNodes->len; i++) { + unsigned int val; + + if (virXMLPropUInt(g_ptr_array_index(binNodes, i), + "start", 10, + VIR_XML_PROP_REQUIRED, + &val) < 0) + return -1; + + if (nbins > 0 && + (val =3D=3D 0 || + val <=3D (*histogram_config)[nbins-1])) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("the values of 'start' attribute of a 'latenc= y-histogram' 'bin' configuration must be sorted and non-overlapping")); + return -1; + } + + if (val > 0) + (*histogram_config)[nbins++] =3D val; + } + + return 0; +} + + +static int +virDomainDiskDefDriverParseXMLHistograms(virDomainDiskDef *def, + xmlNodePtr cur) +{ + g_autoptr(GPtrArray) histogramNodes =3D virXMLNodeGetSubelementList(cu= r, "latency-histogram"); + size_t i; + + for (i =3D 0; i < histogramNodes->len; i++) { + if (virDomainDiskDefDriverParseXMLHistogramOne(def, + g_ptr_array_index(h= istogramNodes, i)) < 0) + return -1; + } + + return 0; +} + + static int virDomainDiskDefDriverParseXML(virDomainDiskDef *def, xmlNodePtr cur) @@ -8384,6 +8474,9 @@ virDomainDiskDefDriverParseXML(virDomainDiskDef *def, return -1; } } + + if (virDomainDiskDefDriverParseXMLHistograms(def, statisticsNode) = < 0) + return -1; } if (virXMLPropEnum(cur, "detect_zeroes", @@ -23987,12 +24080,37 @@ virDomainDiskDefFormatThrottleFilters(virBuffer *= buf, } +static void +virDomainDiskDefFormatDriverHistogram(virBuffer *buf, + const char *type, + unsigned int *bins) +{ + g_auto(virBuffer) histogramAttrBuf =3D VIR_BUFFER_INITIALIZER; + g_auto(virBuffer) histogramChildBuf =3D VIR_BUFFER_INIT_CHILD(buf); + + if (!bins || bins[0] =3D=3D 0) + return; + + if (type) + virBufferAsprintf(&histogramAttrBuf, " type=3D'%s'", type); + + /* we dont store the start boundary of the first bin but it's always t= here */ + virBufferAddLit(&histogramChildBuf, "\n"); + + for (; *bins > 0; bins++) + virBufferAsprintf(&histogramChildBuf, "\n", *bi= ns); + + virXMLFormatElement(buf, "latency-histogram", &histogramAttrBuf, &hist= ogramChildBuf); +} + + static void virDomainDiskDefFormatDriver(virBuffer *buf, virDomainDiskDef *disk) { g_auto(virBuffer) attrBuf =3D VIR_BUFFER_INITIALIZER; g_auto(virBuffer) childBuf =3D VIR_BUFFER_INIT_CHILD(buf); + g_auto(virBuffer) statisticsChildBuf =3D VIR_BUFFER_INIT_CHILD(&childB= uf); virBufferEscapeString(&attrBuf, " name=3D'%s'", virDomainDiskGetDriver= (disk)); @@ -24064,16 +24182,25 @@ virDomainDiskDefFormatDriver(virBuffer *buf, virDomainIothreadMappingDefFormat(&childBuf, disk->iothreads); if (disk->statistics) { - g_auto(virBuffer) statisticsChildBuf =3D VIR_BUFFER_INIT_CHILD(&ch= ildBuf); size_t i; for (i =3D 0; disk->statistics[i] > 0; i++) virBufferAsprintf(&statisticsChildBuf, "\n", disk->statistics[i]); - - virXMLFormatElement(&childBuf, "statistics", NULL, &statisticsChil= dBuf); } + virDomainDiskDefFormatDriverHistogram(&statisticsChildBuf, NULL, + disk->histogram_boundaries); + virDomainDiskDefFormatDriverHistogram(&statisticsChildBuf, "read", + disk->histogram_boundaries_read); + virDomainDiskDefFormatDriverHistogram(&statisticsChildBuf, "write", + disk->histogram_boundaries_write= ); + virDomainDiskDefFormatDriverHistogram(&statisticsChildBuf, "zone", + disk->histogram_boundaries_zone); + virDomainDiskDefFormatDriverHistogram(&statisticsChildBuf, "flush", + disk->histogram_boundaries_flush= ); + + virXMLFormatElement(&childBuf, "statistics", NULL, &statisticsChildBuf= ); virXMLFormatElement(buf, "driver", &attrBuf, &childBuf); } diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 66dc4e3417..b59aca1d6d 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -596,6 +596,13 @@ struct _virDomainDiskDef { GSList *iothreads; /* List of virDomainIothreadMappingDef */ unsigned int *statistics; /* Optional, zero terminated list of interva= ls to collect statistics for */ + /* optional zero terminated lists of bin boundaries for latency histog= rams */ + unsigned int *histogram_boundaries; + unsigned int *histogram_boundaries_read; + unsigned int *histogram_boundaries_write; + unsigned int *histogram_boundaries_zone; + unsigned int *histogram_boundaries_flush; + virDomainDiskDetectZeroes detect_zeroes; virTristateSwitch discard_no_unref; char *domain_name; /* backend domain name */ diff --git a/src/conf/schemas/domaincommon.rng b/src/conf/schemas/domaincom= mon.rng index 8669d8f791..47b087a1d4 100644 --- a/src/conf/schemas/domaincommon.rng +++ b/src/conf/schemas/domaincommon.rng @@ -2741,13 +2741,36 @@ - - - - - - - + + + + + + + + + + + + + + read + write + zone + flush + + + + + + + + + + + + + diff --git a/tests/qemuxmlconfdata/disk-statistics-intervals.x86_64-latest.= xml b/tests/qemuxmlconfdata/disk-statistics-intervals.x86_64-latest.xml index 4c55c50ef5..d02f954073 100644 --- a/tests/qemuxmlconfdata/disk-statistics-intervals.x86_64-latest.xml +++ b/tests/qemuxmlconfdata/disk-statistics-intervals.x86_64-latest.xml @@ -22,6 +22,11 @@ + + + + + @@ -33,6 +38,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/qemuxmlconfdata/disk-statistics-intervals.xml b/tests/qe= muxmlconfdata/disk-statistics-intervals.xml index f5e801f5a8..5f9e9470d7 100644 --- a/tests/qemuxmlconfdata/disk-statistics-intervals.xml +++ b/tests/qemuxmlconfdata/disk-statistics-intervals.xml @@ -19,6 +19,11 @@ + + + + + @@ -29,6 +34,26 @@ + + + + + + + + + + + + + + + + + + + + --=20 2.52.0