From nobody Fri Dec 19 22:07:39 2025 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=1742319702; cv=none; d=zohomail.com; s=zohoarc; b=dgek+COBnVvYMBihLYMHV9+oC1CVkFeEu8CqrACwC2DOoOTJg+uJVWRh57JyV4/znWZ5LY2SkkpWP3+m4mwr31p0Z2/fEI9ji2ros/KDRI8UFFwWxO1yQ601IMMg01SwMXMkAC/dozfAscCEqYFwkP7cf1g3fSzqEinA4fYT4uw= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1742319702; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:Reply-To:Reply-To:References:Subject:Subject:To:To:Message-Id; bh=VH8MbEJS2c5V0KDa1BVI289nCXprnME6V9489fV1Q7Y=; b=ikQudVswAAwJbrXMmkRfaDxamCnsuH6SH8N87Sy1Drp46yeWtmKILLgJkacaf+7jgT/3ZzeWaOAg4tMraMUJo7huXvubpAs2vxhBNgYbz8A1IA5ESykuxu1BYCxPBciekNATn8p9b4TDGFq/SzIcMk7XJlbyCtkLLSp2aKC3gTU= 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 1742319702973944.0546186615558; Tue, 18 Mar 2025 10:41:42 -0700 (PDT) Received: by lists.libvirt.org (Postfix, from userid 996) id 41ACF15CD; Tue, 18 Mar 2025 13:41:42 -0400 (EDT) Received: from lists.libvirt.org (localhost [IPv6:::1]) by lists.libvirt.org (Postfix) with ESMTP id 4913316FD; Tue, 18 Mar 2025 13:35:26 -0400 (EDT) Received: by lists.libvirt.org (Postfix, from userid 996) id 8A5B11751; Tue, 18 Mar 2025 13:35:18 -0400 (EDT) Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by lists.libvirt.org (Postfix) with ESMTPS id BCC1F1746 for ; Tue, 18 Mar 2025 13:34:57 -0400 (EDT) Received: from mx-prod-mc-06.mail-002.prod.us-west-2.aws.redhat.com (ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-460-8nHUumbhM8SrTNP4MhFQCQ-1; Tue, 18 Mar 2025 13:34:54 -0400 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.4]) (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-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 3E633180025F; Tue, 18 Mar 2025 17:34:53 +0000 (UTC) Received: from speedmetal.redhat.com (unknown [10.45.242.6]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 923B93001D0E; Tue, 18 Mar 2025 17:34:51 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on lists.libvirt.org X-Spam-Level: X-Spam-Status: No, score=-0.8 required=5.0 tests=DKIM_INVALID,DKIM_SIGNED, MAILING_LIST_MULTI,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2, RCVD_IN_VALIDITY_RPBL_BLOCKED,RCVD_IN_VALIDITY_SAFE_BLOCKED, SPF_HELO_NONE autolearn=unavailable autolearn_force=no version=3.4.4 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1742319297; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=66wLXyo6zd8ewtYZbWfzx1YauDyjz4YIXW0VkqDvnTY=; b=HYajWXF6jWeBiokd1EjbwVMchHz0h+GlAK3DpzxpJvoKyzMAXVnZGGKhs4lu0Pb9MjpZ4a uul+RNG+rLH6k99NXnwRku+OhFWtvgxEUye+/UIxSpyIyGtpuVce+M7sD+3KfFWvlelIkG /IU4FKrkQ0P1d/GTQu09nfcWZSeUqYQ= X-MC-Unique: 8nHUumbhM8SrTNP4MhFQCQ-1 X-Mimecast-MFC-AGG-ID: 8nHUumbhM8SrTNP4MhFQCQ_1742319293 To: devel@lists.libvirt.org Subject: [PATCH v9 10/17] qemu: helper: throttle filter nodename and preparation processing Date: Tue, 18 Mar 2025 18:34:24 +0100 Message-ID: In-Reply-To: References: MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: pbGa-IuEiW9TRNKfi5LTeMkbRrvL8KsCaVzFQYVrXWY_1742319293 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: quoted-printable Message-ID-Hash: OCOEWK4SZVS5RPA5YM6LXKQUB3IKAMPS X-Message-ID-Hash: OCOEWK4SZVS5RPA5YM6LXKQUB3IKAMPS X-MailFrom: pkrempa@redhat.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; header-match-config-1; header-match-config-2; header-match-config-3; header-match-devel.lists.libvirt.org-0; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; suspicious-header CC: Harikumar Rajkumar , harikumar.rajkumar@ibm.com X-Mailman-Version: 3.2.2 Precedence: list List-Id: Development discussions about the libvirt library & tools Archived-At: List-Archive: List-Help: 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: 1742319705345019000 Content-Type: text/plain; charset="utf-8" From: Chun Feng Wu It contains throttle filter nodename processing(new nodename, topnodename, parse and format nodename), throttle filter attaching/detaching preparation and implementation. * Updated "qemuDomainDiskGetTopNodename", so if throttlefilter is used together with copyOnRead, top node is throttle filter node, e.g. device -> throttle -> copyOnRead Layer-> image chain * In qemuBuildThrottleFiltersAttachPrepareBlockdev, if copy_on_read is on, build throttle nodename chain on top of copy_on_read nodename * In status xml, throttle filter nodename(virDomainDiskDef.nodename) is saved at disk/privateData/nodenames/nodename(type=3D'throttle-filter'), corresponding parse/format sits in qemuDomainDiskPrivateParse and qemuDomainDiskPrivateFormat * If filter nodename hasn't been set by qemuDomainDiskPrivateParse, in qemuDomainPrepareThrottleFilterBlockdev, filter nodename index can be generated by reusing qemuDomainStorageIDNew and current global sequence number is persistented in virDomainObj- >privateData(qemuDomainObjPrivate)->nodenameindex. qemuDomainPrepareThrottleFilterBlockdev is called by qemuDomainPrepareDiskSourceBlockdev, which in turn used by both hotplug and qemuProcessStart to prepare throttle filter node name * Define method qemuBlockThrottleFilterGetProps, which is used by both hotplug and command to build throttle object for QEMU * Define methods for throttle filter attach/detach/rollback Signed-off-by: Chun Feng Wu * Apply suggested coding style changes. * Update of code documentation comments. Signed-off-by: Harikumar Rajkumar Reviewed-by: Peter Krempa Signed-off-by: Peter Krempa --- src/qemu/qemu_block.c | 136 ++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_block.h | 49 +++++++++++++++ src/qemu/qemu_command.c | 81 ++++++++++++++++++++++++ src/qemu/qemu_command.h | 6 ++ src/qemu/qemu_domain.c | 77 +++++++++++++++++++++-- 5 files changed, 345 insertions(+), 4 deletions(-) diff --git a/src/qemu/qemu_block.c b/src/qemu/qemu_block.c index 2468725bf7..e370411e16 100644 --- a/src/qemu/qemu_block.c +++ b/src/qemu/qemu_block.c @@ -2743,6 +2743,142 @@ qemuBlockStorageSourceCreateDetectSize(GHashTable *= blockNamedNodeData, } +void +qemuBlockThrottleFilterSetNodename(virDomainThrottleFilterDef *filter, + char *nodename) +{ + g_free(filter->nodename); + filter->nodename =3D nodename; +} + + +const char * +qemuBlockThrottleFilterGetNodename(virDomainThrottleFilterDef *filter) +{ + return filter->nodename; +} + + +/** + * qemuBlockThrottleFilterGetProps: + * @filter: throttle filter + * @parentNodeName: parent nodename of @filter + * + * Build "arguments" part of "blockdev-add" QMP cmd. + */ +static inline virJSONValue * +qemuBlockThrottleFilterGetProps(virDomainThrottleFilterDef *filter, + const char *parentNodeName) +{ + g_autoptr(virJSONValue) props =3D NULL; + /* prefix group name with "throttle-" in QOM */ + g_autofree char *prefixed_group_name =3D g_strdup_printf("throttle-%s"= , filter->group_name); + if (virJSONValueObjectAdd(&props, + "s:driver", "throttle", + "s:node-name", qemuBlockThrottleFilterGetNod= ename(filter), + "s:throttle-group", prefixed_group_name, + "s:file", parentNodeName, + NULL) < 0) + return NULL; + + return g_steal_pointer(&props); +} + + +void +qemuBlockThrottleFilterAttachDataFree(qemuBlockThrottleFilterAttachData *d= ata) +{ + if (!data) + return; + + virJSONValueFree(data->filterProps); + g_free(data); +} + + +qemuBlockThrottleFilterAttachData * +qemuBlockThrottleFilterAttachPrepareBlockdev(virDomainThrottleFilterDef *f= ilter, + const char *parentNodeName) +{ + g_autoptr(qemuBlockThrottleFilterAttachData) data =3D NULL; + + data =3D g_new0(qemuBlockThrottleFilterAttachData, 1); + + if (!(data->filterProps =3D qemuBlockThrottleFilterGetProps(filter, pa= rentNodeName))) + return NULL; + + data->filterNodeName =3D qemuBlockThrottleFilterGetNodename(filter); + + return g_steal_pointer(&data); +} + + +void +qemuBlockThrottleFilterAttachRollback(qemuMonitor *mon, + qemuBlockThrottleFilterAttachData *d= ata) +{ + virErrorPtr orig_err; + + virErrorPreserveLast(&orig_err); + + if (data->filterAttached) + ignore_value(qemuMonitorBlockdevDel(mon, data->filterNodeName)); + + virErrorRestore(&orig_err); +} + + +void +qemuBlockThrottleFiltersDataFree(qemuBlockThrottleFiltersData *data) +{ + size_t i; + + if (!data) + return; + + for (i =3D 0; i < data->nfilterdata; i++) + qemuBlockThrottleFilterAttachDataFree(data->filterdata[i]); + + g_free(data->filterdata); + g_free(data); +} + + +/** + * qemuBlockThrottleFiltersAttach: + * @mon: monitor object + * @data: filter chain data + * + * Attach throttle filters. + * Caller must enter @mon prior calling this function. + */ +int +qemuBlockThrottleFiltersAttach(qemuMonitor *mon, + qemuBlockThrottleFiltersData *data) +{ + size_t i; + + for (i =3D 0; i < data->nfilterdata; i++) { + if (qemuMonitorBlockdevAdd(mon, &data->filterdata[i]->filterProps)= < 0) + return -1; + data->filterdata[i]->filterAttached =3D true; + } + + return 0; +} + + +void +qemuBlockThrottleFiltersDetach(qemuMonitor *mon, + qemuBlockThrottleFiltersData *data) +{ + size_t i; + + for (i =3D data->nfilterdata; i > 0; i--) + qemuBlockThrottleFilterAttachRollback(mon, data->filterdata[i-1]); +} + + int qemuBlockRemoveImageMetadata(virQEMUDriver *driver, virDomainObj *vm, diff --git a/src/qemu/qemu_block.h b/src/qemu/qemu_block.h index f13a4a4a9a..b9e950e494 100644 --- a/src/qemu/qemu_block.h +++ b/src/qemu/qemu_block.h @@ -215,6 +215,55 @@ qemuBlockStorageSourceCreateDetectSize(GHashTable *blo= ckNamedNodeData, virStorageSource *src, virStorageSource *templ); +void +qemuBlockThrottleFilterSetNodename(virDomainThrottleFilterDef *filter, + char *nodename); + +const char * +qemuBlockThrottleFilterGetNodename(virDomainThrottleFilterDef *filter); + +typedef struct qemuBlockThrottleFilterAttachData qemuBlockThrottleFilterAt= tachData; +struct qemuBlockThrottleFilterAttachData { + virJSONValue *filterProps; + const char *filterNodeName; + bool filterAttached; +}; + +qemuBlockThrottleFilterAttachData * +qemuBlockThrottleFilterAttachPrepareBlockdev(virDomainThrottleFilterDef *t= hrottlefilter, + const char *parentNodeName); + +void +qemuBlockThrottleFilterAttachDataFree(qemuBlockThrottleFilterAttachData *d= ata); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(qemuBlockThrottleFilterAttachData, + qemuBlockThrottleFilterAttachDataFree); + +void +qemuBlockThrottleFilterAttachRollback(qemuMonitor *mon, + qemuBlockThrottleFilterAttachData *d= ata); + +struct _qemuBlockThrottleFiltersData { + qemuBlockThrottleFilterAttachData **filterdata; + size_t nfilterdata; +}; + +typedef struct _qemuBlockThrottleFiltersData qemuBlockThrottleFiltersData; + +void +qemuBlockThrottleFiltersDataFree(qemuBlockThrottleFiltersData *data); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(qemuBlockThrottleFiltersData, + qemuBlockThrottleFiltersDataFree); + +int +qemuBlockThrottleFiltersAttach(qemuMonitor *mon, + qemuBlockThrottleFiltersData *data); + +void +qemuBlockThrottleFiltersDetach(qemuMonitor *mon, + qemuBlockThrottleFiltersData *data); + int qemuBlockRemoveImageMetadata(virQEMUDriver *driver, virDomainObj *vm, diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 84ff62cd6c..ce5f45b66b 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -11021,6 +11021,87 @@ qemuBuildStorageSourceChainAttachPrepareBlockdevOn= e(qemuBlockStorageSourceChainD } +/** + * qemuBuildThrottleFiltersAttachPrepareBlockdevOne: + * @data: filter chain data, which consists of array of filters and size o= f such array + * @throttlefilter: new filter to be added into filter array + * @parentNodeName: parent nodename for this new throttlefilter + * + * Build filter node chain to provide more flexibility for block disk I/O = limits + */ +static int +qemuBuildThrottleFiltersAttachPrepareBlockdevOne(qemuBlockThrottleFiltersD= ata *data, + virDomainThrottleFilterDe= f *throttlefilter, + const char *parentNodeNam= e) +{ + g_autoptr(qemuBlockThrottleFilterAttachData) elem =3D NULL; + + if (!(elem =3D qemuBlockThrottleFilterAttachPrepareBlockdev(throttlefi= lter, parentNodeName))) + return -1; + + VIR_APPEND_ELEMENT(data->filterdata, data->nfilterdata, elem); + return 0; +} + + +/** + * qemuBuildThrottleFiltersAttachPrepareBlockdev: + * @disk: domain disk + * + * Build filter node chain to provide more flexibility for block disk I/O = limits + */ +qemuBlockThrottleFiltersData * +qemuBuildThrottleFiltersAttachPrepareBlockdev(virDomainDiskDef *disk) +{ + g_autoptr(qemuBlockThrottleFiltersData) data =3D NULL; + size_t i; + const char *parentNodeName =3D NULL; + qemuDomainDiskPrivate *priv =3D QEMU_DOMAIN_DISK_PRIVATE(disk); + + data =3D g_new0(qemuBlockThrottleFiltersData, 1); + /* if copy_on_read is enabled, put throttle chain on top of it */ + if (disk->copy_on_read =3D=3D VIR_TRISTATE_SWITCH_ON) { + parentNodeName =3D priv->nodeCopyOnRead; + } else { + parentNodeName =3D qemuBlockStorageSourceGetEffectiveNodename(disk= ->src); + + } + /* build filterdata, which contains all filters info and sequence info= through parentNodeName */ + for (i =3D 0; i < disk->nthrottlefilters; i++) { + if (qemuBuildThrottleFiltersAttachPrepareBlockdevOne(data, disk->t= hrottlefilters[i], parentNodeName) < 0) + return NULL; + parentNodeName =3D disk->throttlefilters[i]->nodename; + } + + return g_steal_pointer(&data); +} + + +/** + * qemuBuildThrottleFiltersDetachPrepareBlockdev: + * @disk: domain disk + * + * Build filters data for later "blockdev-del" + */ +qemuBlockThrottleFiltersData * +qemuBuildThrottleFiltersDetachPrepareBlockdev(virDomainDiskDef *disk) +{ + g_autoptr(qemuBlockThrottleFiltersData) data =3D g_new0(qemuBlockThrot= tleFiltersData, 1); + size_t i; + + /* build filterdata, which contains filters info and sequence info */ + for (i =3D 0; i < disk->nthrottlefilters; i++) { + g_autoptr(qemuBlockThrottleFilterAttachData) elem =3D g_new0(qemuB= lockThrottleFilterAttachData, 1); + /* ignore other fields since the following info are enough for "bl= ockdev-del" */ + elem->filterNodeName =3D qemuBlockThrottleFilterGetNodename(disk->= throttlefilters[i]); + elem->filterAttached =3D true; + + VIR_APPEND_ELEMENT(data->filterdata, data->nfilterdata, elem); + } + return g_steal_pointer(&data); +} + + /** * qemuBuildStorageSourceChainAttachPrepareBlockdev: * @top: storage source chain diff --git a/src/qemu/qemu_command.h b/src/qemu/qemu_command.h index 903ffa62ef..79d4f47690 100644 --- a/src/qemu/qemu_command.h +++ b/src/qemu/qemu_command.h @@ -124,6 +124,12 @@ qemuBlockStorageSourceChainData * qemuBuildStorageSourceChainAttachPrepareBlockdevTop(virStorageSource *top, virStorageSource *back= ingStore); +qemuBlockThrottleFiltersData * +qemuBuildThrottleFiltersAttachPrepareBlockdev(virDomainDiskDef *disk); + +qemuBlockThrottleFiltersData * +qemuBuildThrottleFiltersDetachPrepareBlockdev(virDomainDiskDef *disk); + virJSONValue * qemuBuildDiskDeviceProps(const virDomainDef *def, virDomainDiskDef *disk, diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index efc1ebddd5..fcdf28f3fc 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -2236,6 +2236,33 @@ qemuStorageSourcePrivateDataFormat(virStorageSource = *src, } +static int +virDomainDiskThrottleFilterNodeNamesParse(xmlXPathContextPtr ctxt, + virDomainDiskDef *def) +{ + size_t i; + int n =3D 0; + g_autofree xmlNodePtr *nodes =3D NULL; + g_autoptr(GHashTable) throttleFiltersMap =3D virHashNew(g_free); + + if ((n =3D virXPathNodeSet("./nodenames/nodename[@type=3D'throttle-fil= ter']", ctxt, &nodes)) < 0) + return -1; + + for (i =3D 0; i < n; i++) { + g_hash_table_insert(throttleFiltersMap, virXMLPropString(nodes[i],= "group"), virXMLPropString(nodes[i], "name")); + } + + for (i =3D 0; i < def->nthrottlefilters; i++) { + char *nodename =3D g_hash_table_lookup(throttleFiltersMap, def->th= rottlefilters[i]->group_name); + if (nodename) { + qemuBlockThrottleFilterSetNodename(def->throttlefilters[i], g_= strdup(nodename)); + } + } + + return 0; +} + + static int qemuDomainDiskPrivateParse(xmlXPathContextPtr ctxt, virDomainDiskDef *disk) @@ -2245,6 +2272,9 @@ qemuDomainDiskPrivateParse(xmlXPathContextPtr ctxt, priv->qomName =3D virXPathString("string(./qom/@name)", ctxt); priv->nodeCopyOnRead =3D virXPathString("string(./nodenames/nodename[@= type=3D'copyOnRead']/@name)", ctxt); + if (virDomainDiskThrottleFilterNodeNamesParse(ctxt, disk) < 0) + return -1; + return 0; } @@ -2254,14 +2284,27 @@ qemuDomainDiskPrivateFormat(virDomainDiskDef *disk, virBuffer *buf) { qemuDomainDiskPrivate *priv =3D QEMU_DOMAIN_DISK_PRIVATE(disk); + size_t i; virBufferEscapeString(buf, "\n", priv->qomName); - if (priv->nodeCopyOnRead) { + if (priv->nodeCopyOnRead || disk->nthrottlefilters > 0) { virBufferAddLit(buf, "\n"); virBufferAdjustIndent(buf, 2); - virBufferEscapeString(buf, "\n", - priv->nodeCopyOnRead); + if (priv->nodeCopyOnRead) + virBufferEscapeString(buf, "\n", + priv->nodeCopyOnRead); + if (disk->nthrottlefilters > 0) { + for (i =3D 0; i < disk->nthrottlefilters; i++) { + + if (disk->throttlefilters[i]->nodename) + virBufferEscapeString(buf, "throttlefilters[i]->nodena= me); + + if (disk->throttlefilters[i]->group_name) + virBufferEscapeString(buf, "group=3D'%s'/>\n", disk->t= hrottlefilters[i]->group_name); + } + } virBufferAdjustIndent(buf, -2); virBufferAddLit(buf, "\n"); } @@ -6292,7 +6335,8 @@ qemuDomainDetermineDiskChain(virQEMUDriver *driver, * @disk: disk definition object * * Returns the pointer to the node-name of the topmost layer used by @disk= as - * backend. Currently returns the nodename of the copy-on-read filter if e= nabled + * backend. Currently returns the nodename of top throttle filter if enabl= ed + * or the nodename of the copy-on-read filter if enabled * or the nodename of the top image's format driver. Empty disks return NU= LL. * This must be used only with disks instantiated via -blockdev (thus not * for SD cards). @@ -6305,6 +6349,10 @@ qemuDomainDiskGetTopNodename(virDomainDiskDef *disk) if (virStorageSourceIsEmpty(disk->src)) return NULL; + /* If disk has throttles, take top throttle node name */ + if (disk->nthrottlefilters > 0) + return disk->throttlefilters[disk->nthrottlefilters - 1]->nodename; + if (disk->copy_on_read =3D=3D VIR_TRISTATE_SWITCH_ON) return priv->nodeCopyOnRead; @@ -9707,6 +9755,22 @@ qemuDomainPrepareStorageSourceBlockdevNodename(virDo= mainDiskDef *disk, } +static void +qemuDomainPrepareThrottleFilterBlockdev(virDomainThrottleFilterDef *filter, + qemuDomainObjPrivate *priv) +{ + g_autofree char *nodenameprefix =3D NULL; + + /* skip setting throttle filter nodename if it's set by parsing status= xml */ + if (filter->nodename) { + return; + } + nodenameprefix =3D g_strdup_printf("libvirt-%u", qemuDomainStorageIDNe= w(priv)); + + qemuBlockThrottleFilterSetNodename(filter, g_strdup_printf("%s-filter"= , nodenameprefix)); +} + + int qemuDomainPrepareStorageSourceBlockdev(virDomainDiskDef *disk, virStorageSource *src, @@ -9730,6 +9794,7 @@ qemuDomainPrepareDiskSourceBlockdev(virDomainDiskDef = *disk, { qemuDomainDiskPrivate *diskPriv =3D QEMU_DOMAIN_DISK_PRIVATE(disk); virStorageSource *n; + size_t i; if (disk->copy_on_read =3D=3D VIR_TRISTATE_SWITCH_ON && !diskPriv->nodeCopyOnRead) @@ -9744,6 +9809,10 @@ qemuDomainPrepareDiskSourceBlockdev(virDomainDiskDef= *disk, return -1; } + for (i =3D 0; i < disk->nthrottlefilters; i++) { + qemuDomainPrepareThrottleFilterBlockdev(disk->throttlefilters[i], = priv); + } + return 0; } --=20 2.48.1