From nobody Sun Dec 7 13:20:06 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=fail(p=none dis=none) header.from=gmail.com Return-Path: Received: from lists.libvirt.org (lists.libvirt.org [8.43.85.245]) by mx.zohomail.com with SMTPS id 17399849440751009.4069396761199; Wed, 19 Feb 2025 09:09:04 -0800 (PST) Received: by lists.libvirt.org (Postfix, from userid 996) id 077DA1DDE; Wed, 19 Feb 2025 12:09:02 -0500 (EST) Received: from lists.libvirt.org (localhost [IPv6:::1]) by lists.libvirt.org (Postfix) with ESMTP id DF1B01CD6; Wed, 19 Feb 2025 12:02:30 -0500 (EST) Received: by lists.libvirt.org (Postfix, from userid 996) id 1C63C1A61; Wed, 19 Feb 2025 12:02:25 -0500 (EST) Received: from mail-pl1-f175.google.com (mail-pl1-f175.google.com [209.85.214.175]) (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 89F3F1CFF for ; Wed, 19 Feb 2025 12:02:04 -0500 (EST) Received: by mail-pl1-f175.google.com with SMTP id d9443c01a7336-22113560c57so8589105ad.2 for ; Wed, 19 Feb 2025 09:02:04 -0800 (PST) Received: from localhost.localdomain ([2001:4490:4ea9:8374:8512:e327:fdaf:a56e]) by smtp.googlemail.com with ESMTPSA id d9443c01a7336-220d558fe3asm106651545ad.234.2025.02.19.09.01.59 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Wed, 19 Feb 2025 09:02:00 -0800 (PST) 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_ADSP_CUSTOM_MED, DKIM_INVALID,DKIM_SIGNED,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,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=gmail.com; s=20230601; t=1739984523; x=1740589323; darn=lists.libvirt.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=nIL//KDNYdpfqP8ilBIiiM155zL7+Po2Tgi3BpucS+4=; b=GIsghkhKBxbH/wTdXDuG+gKIXlVr4lYw7TY6vlrfymAgl+sXvEa9te1+XuxS5oUIrN 06SBacoZavbWgU2jYtcl6ilITG/gW/D4W3luDYigNZN5CMoOf0RAdM+8Vvgw+lm6hfyu sLULKyP3sQVREy+ZGo1so5xqkdmR6Gs0cCA5/ouLEpiLufxzbNMaVaqL66AST8F4CscC EWl82cKZGW5GFxazOyTa15vxTR0kau90bdbWgxBMaVjd9+QVQkr7Sgli8zTVgrCMktbV AWZeWQiUjjAtCv1t4mTBbg0lkLXX4npx4KFcpkR1pvZiw77rgugmUJJPbfPHI40ydevp Co/A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1739984523; x=1740589323; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=nIL//KDNYdpfqP8ilBIiiM155zL7+Po2Tgi3BpucS+4=; b=Weh8LDcUjCnp0oWnyQEmRuPBN+SSAVj92eOxZP7pBu05o4SrEIIrPdHzRMtrMGpkUT n+za1vuLmpaYYtP1aGcCLVynFE2aTW2Q1sYsdafG7+ysrvfwOygyuJV5TNIUI/8BM4gT 5dQbuDqkR7cBvQVrSKIi1/XDqZ+uXQdfDjv+rh2SkZ3KB+TXdG79PiqN1MoybSTinqVk hNm0v6swogoDp0O4K+cjWKOuuXszos1fgEfEInxfM604nY9npYc8dRANJvfF025iO9gD r1Z5t58EYO8C/9naegy/N4hHjymhGKaIKDd7RWgutrdapq54+n7FHmI0SDrlb8HOlmq7 99bw== X-Gm-Message-State: AOJu0YzwkMnCGd0aQl7uKNkIffCMVsI4U6UJsZynIbqk8/Qt8sDHfQdb b2MfvViMyjo/f6mB1uktspu6eGC7yfZ0ewcbjI+LZOT78jV+pc7kTL0APVwv X-Gm-Gg: ASbGncsDVZEmj1j4oFzjtZar7k5e686Qxg6q8l2nNK84eusqhPieYBDUq/WhDaM7Onw kEYJU01t7fnelsykZj521enZmtLZxECvDuNlgAgn++OLZwQNMDfwqx/f1lWG4iW4i93sNXD0WYp lHG22Im7LXOOrIZdBn15Av3rkhp4JHq6OnROzbgRSdIt1E1u02gyn9U0fz26FyQ2XEZ2jozcJFA oVERQ2wJbdRApAM43yV6t/JjbfGfKP0v+luPdJB6W5v6oc0m0nyMxTp7BOCdNPIeZPb9WW+gE6s gzDzXywTMu4aNVDwWJUcAA5cd3AXGrT+/UeHie+Z4KYTB01wgGrdxVLqqA== X-Google-Smtp-Source: AGHT+IGdmN2WSsKo5+lpeSSrzApo5ca1nykCi0Bh53vlbKtgb/l83PNOvQK69Y/StbQ6uN6BjT/YFg== X-Received: by 2002:a17:902:d542:b0:216:644f:bc0e with SMTP id d9443c01a7336-2210404f783mr366477865ad.24.1739984521492; Wed, 19 Feb 2025 09:02:01 -0800 (PST) From: Harikumar Rajkumar To: devel@lists.libvirt.org Subject: [PATCH v8 10/18] qemu: helper: throttle filter nodename and preparation processing Date: Wed, 19 Feb 2025 22:27:14 +0530 Message-Id: <20250219165722.26348-11-harirajkumar230@gmail.com> X-Mailer: git-send-email 2.39.5 (Apple Git-154) In-Reply-To: <20250219165722.26348-1-harirajkumar230@gmail.com> References: <20250219165722.26348-1-harirajkumar230@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Message-ID-Hash: MQRJW3A2WZWQRMPXP2NUAB7OORUPKE72 X-Message-ID-Hash: MQRJW3A2WZWQRMPXP2NUAB7OORUPKE72 X-MailFrom: harirajkumar230@gmail.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: sanjeev.ranjan@ibm.com, harikumar.rajkumar@ibm.com, earulana@in.ibm.com, Chun Feng Wu , Harikumar Rajkumar 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: X-ZohoMail-DKIM: fail (Header signature does not verify) X-ZM-MESSAGEID: 1739984946413019100 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 --- 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 35dca8ee7b..254ea87465 100644 --- a/src/qemu/qemu_block.c +++ b/src/qemu/qemu_block.c @@ -2775,6 +2775,142 @@ qemuBlockStorageSourceCreateDetectSize(GHashTable *= blockNamedNodeData, } =20 =20 +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); =20 +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 54130ac4f0..750edd4faa 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -11043,6 +11043,87 @@ qemuBuildStorageSourceChainAttachPrepareBlockdevOn= e(qemuBlockStorageSourceChainD } =20 =20 +/** + * 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 76c514b5f7..1b0296ea42 100644 --- a/src/qemu/qemu_command.h +++ b/src/qemu/qemu_command.h @@ -116,6 +116,12 @@ qemuBlockStorageSourceChainData * qemuBuildStorageSourceChainAttachPrepareBlockdevTop(virStorageSource *top, virStorageSource *back= ingStore); =20 +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 df1ed0223d..5ef7e3782f 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -2235,6 +2235,33 @@ qemuStorageSourcePrivateDataFormat(virStorageSource = *src, } =20 =20 +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) @@ -2244,6 +2271,9 @@ qemuDomainDiskPrivateParse(xmlXPathContextPtr ctxt, priv->qomName =3D virXPathString("string(./qom/@name)", ctxt); priv->nodeCopyOnRead =3D virXPathString("string(./nodenames/nodename[@= type=3D'copyOnRead']/@name)", ctxt); =20 + if (virDomainDiskThrottleFilterNodeNamesParse(ctxt, disk) < 0) + return -1; + return 0; } =20 @@ -2253,14 +2283,27 @@ qemuDomainDiskPrivateFormat(virDomainDiskDef *disk, virBuffer *buf) { qemuDomainDiskPrivate *priv =3D QEMU_DOMAIN_DISK_PRIVATE(disk); + size_t i; =20 virBufferEscapeString(buf, "\n", priv->qomName); =20 - 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"); } @@ -6293,7 +6336,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). @@ -6306,6 +6350,10 @@ qemuDomainDiskGetTopNodename(virDomainDiskDef *disk) if (virStorageSourceIsEmpty(disk->src)) return NULL; =20 + /* 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; =20 @@ -9708,6 +9756,22 @@ qemuDomainPrepareStorageSourceBlockdevNodename(virDo= mainDiskDef *disk, } =20 =20 +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, @@ -9731,6 +9795,7 @@ qemuDomainPrepareDiskSourceBlockdev(virDomainDiskDef = *disk, { qemuDomainDiskPrivate *diskPriv =3D QEMU_DOMAIN_DISK_PRIVATE(disk); virStorageSource *n; + size_t i; =20 if (disk->copy_on_read =3D=3D VIR_TRISTATE_SWITCH_ON && !diskPriv->nodeCopyOnRead) @@ -9745,6 +9810,10 @@ qemuDomainPrepareDiskSourceBlockdev(virDomainDiskDef= *disk, return -1; } =20 + for (i =3D 0; i < disk->nthrottlefilters; i++) { + qemuDomainPrepareThrottleFilterBlockdev(disk->throttlefilters[i], = priv); + } + return 0; } =20 --=20 2.39.5 (Apple Git-154)