From nobody Sat Nov 23 11:35:08 2024 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 173193945450859.79929765477368; Mon, 18 Nov 2024 06:17:34 -0800 (PST) Received: by lists.libvirt.org (Postfix, from userid 996) id 59BB01B8A; Mon, 18 Nov 2024 09:17:33 -0500 (EST) Received: from lists.libvirt.org (localhost [IPv6:::1]) by lists.libvirt.org (Postfix) with ESMTP id 8D8391BE4; Mon, 18 Nov 2024 09:10:42 -0500 (EST) Received: by lists.libvirt.org (Postfix, from userid 996) id D9D011A06; Mon, 18 Nov 2024 08:55:14 -0500 (EST) Received: from mail-pf1-f173.google.com (mail-pf1-f173.google.com [209.85.210.173]) (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 AE6491A01 for ; Mon, 18 Nov 2024 08:55:13 -0500 (EST) Received: by mail-pf1-f173.google.com with SMTP id d2e1a72fcca58-720c286bcd6so3362647b3a.3 for ; Mon, 18 Nov 2024 05:55:13 -0800 (PST) Received: from localhost.localdomain ([2409:40f4:9:7d39:e991:fcfb:15ab:ce58]) by smtp.googlemail.com with ESMTPSA id d2e1a72fcca58-72477135ecbsm6113482b3a.84.2024.11.18.05.55.09 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Mon, 18 Nov 2024 05:55:11 -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.5 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_H3,RCVD_IN_MSPIKE_WL,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=1731938112; x=1732542912; 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=S1iXm4qwXe/uBgKVONYLwEEt+G199VzByUQedLLD/Mg=; b=DR5l39Bl6gwpTzVXMBVNVRw3b28DaVPP7KnxQM2IApKVcWyba6u+PkxQA2bTRrOcaQ 4Q9iPBmioJJkfvdkgFzo13/lJ7VziV+vHj0y1DMvhktF/WODEBiLIg5TWXOxU34M4i7I G+C1VukZQZxgN+1lZm5d0nhDB8IHDVIwW2KKcnkR+HQyLo5B/ehIbgHSyS1R2y+Gya7i +UM3oaUntBCaDkxxOfCwpblLY8MJ0isu5Sw83kyAtrQpmB2i8q+HEm/lNNEwZ5d28ZQt kvh8JX1J28Khwcw95rxAo1EhEYY00xG/tUwLZLPODaOQKrHFZDm4GddPMyBBwE46ijzJ cZvQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1731938112; x=1732542912; 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=S1iXm4qwXe/uBgKVONYLwEEt+G199VzByUQedLLD/Mg=; b=OSa5tVOAfq0oVMT3mshZ8s2vc+afF9DTmCSbc3Gip92TVjFnpt53VCti4SUfBxyFd8 /SzIzO8L4Oo9CJZ9p7xS3mu//VMLu68XQfWOFfV4xmQojZ5AizAt8rTNdjNGtqZhHEMQ uYOvO6ZYFzwgPyJ/BpwQ4xwPbyPYFjet9fiyFIzv0xlX3MCgfbwRLvHagDFIUOREOi9T LYaaRO+BlPcg0goT/NmtY/SjNQ5nZ6W/MEahDFcv+x7Q1USfvQaKRltIomhJsXz8+oJH f5XUq5WdvrDdsOfyLLFnc3i8drEwkFNsiDc+DQ6wxJyiAhEBH4UcXdLke9VHWno6dI0X M85A== X-Gm-Message-State: AOJu0Yy/aMxTK4l3b2F/TNyfHoq7l7+6yngKh0gBFcKyrQtKfyBzJ4jj 8xidiVFSEudHGQlzSHbGySh8k7u1291OwbNPXusQbJ/aawAoSv7I4pQBDtWU X-Google-Smtp-Source: AGHT+IERSGB/LOdKruY94UR8CtSjAxWN+JZgZirgvnjjEa73NyJe04jNQrmTwq2VWCdazgFr6oUQWg== X-Received: by 2002:a05:6a00:c84:b0:71e:4196:ddb2 with SMTP id d2e1a72fcca58-72476ba7077mr16641262b3a.9.1731938111660; Mon, 18 Nov 2024 05:55:11 -0800 (PST) From: Harikumar R To: devel@lists.libvirt.org Subject: [PATCH v5 10/18] qemu: helper: throttle filter nodename and preparation processing Date: Mon, 18 Nov 2024 19:24:18 +0530 Message-Id: <20241118135426.37643-11-harirajkumar230@gmail.com> X-Mailer: git-send-email 2.39.5 (Apple Git-154) In-Reply-To: <20241118135426.37643-1-harirajkumar230@gmail.com> References: <20241118135426.37643-1-harirajkumar230@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-MailFrom: harirajkumar230@gmail.com X-Mailman-Rule-Hits: nonmember-moderation 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 Message-ID-Hash: M5BVPGP6F5MEQ3U5R4H6TN5FSAMRSH4L X-Message-ID-Hash: M5BVPGP6F5MEQ3U5R4H6TN5FSAMRSH4L X-Mailman-Approved-At: Mon, 18 Nov 2024 14:10:18 -0500 CC: harikumar.rajkumar@ibm.com, sanjeev.ranjan@ibm.com, earulana@in.ibm.com, Chun Feng Wu 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: 1731939455103116600 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 --- 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 | 73 +++++++++++++++++++-- 5 files changed, 341 insertions(+), 4 deletions(-) diff --git a/src/qemu/qemu_block.c b/src/qemu/qemu_block.c index 692b4d350e..5ff7319ceb 100644 --- a/src/qemu/qemu_block.c +++ b/src/qemu/qemu_block.c @@ -2755,6 +2755,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 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 696f891b47..0c119c8827 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -10996,6 +10996,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 { + /* get starting parentNodename, e.g. libvirt-1-format */ + 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 f3b810a564..b6acf895c3 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -2235,6 +2235,34 @@ 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) { + /* first time to set nodename in filter */ + def->throttlefilters[i]->nodename =3D g_strdup(nodename); + } + } + + return 0; +} + + static int qemuDomainDiskPrivateParse(xmlXPathContextPtr ctxt, virDomainDiskDef *disk) @@ -2244,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); =20 + if (virDomainDiskThrottleFilterNodeNamesParse(ctxt, disk) < 0) + return -1; + return 0; } =20 @@ -2253,14 +2284,22 @@ 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++) { + virBufferEscapeString(buf, "throttlefilters[i]->nodename); + virBufferEscapeString(buf, "group=3D'%s'/>\n", disk->throt= tlefilters[i]->group_name); + } + } virBufferAdjustIndent(buf, -2); virBufferAddLit(buf, "\n"); } @@ -6348,7 +6387,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). @@ -6361,6 +6401,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 @@ -9724,6 +9768,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)); + /* generate and set nodename into filter for later QEMU cmd preparatio= n */ + qemuBlockThrottleFilterSetNodename(filter, g_strdup_printf("%s-filter"= , nodenameprefix)); +} + + int qemuDomainPrepareStorageSourceBlockdev(virDomainDiskDef *disk, virStorageSource *src, @@ -9747,6 +9807,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) @@ -9757,6 +9818,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)