From nobody Sun Apr 19 12:43:19 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.libvirt.org designates 38.145.34.151 as permitted sender) client-ip=38.145.34.151; 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 38.145.34.151 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=1776357070; cv=none; d=zohomail.com; s=zohoarc; b=cpTytR/hp98w6FkxDRBT+nvjGlcwdn/yCWpLRqsewZES5ltL9ZkrcERyRoNx1H+fmwQ2KVeNvHdGJg6bu7r8JQ8YvqjHeKw6Kk6YHmGwF9fXw/uyZn3CaTzGFrTr78Vse8RSBMf0jUS0M0BOaDIc1V63jTIu244Mtzu257urgg8= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1776357070; h=Content-Transfer-Encoding:Cc:Cc: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; bh=MqnRFPQU5e6Llp95YhrMt1RlqPiyhDUNzlnKzMjT8ms=; b=LOmyEl7uPLvH4N8jwEGcsz4a35vEMuc8/bQHpCMmPXK4rfwkuMbYw9q2NnufJt1KvDJYG7WjN8kKaR5M4wH6HmocsoiEXIePfIgmW1QuKY5OxSnec2nlIA+/abNKyJ5maqfOgzSS9JquZyTKGcHN98PAzdJ2Ic/s4dW5M94CkQ4= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of lists.libvirt.org designates 38.145.34.151 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 [38.145.34.151]) by mx.zohomail.com with SMTPS id 177635707087898.13752952555467; Thu, 16 Apr 2026 09:31:10 -0700 (PDT) Received: by lists.libvirt.org (Postfix, from userid 993) id D13B541804; Thu, 16 Apr 2026 12:31:08 -0400 (EDT) Received: from [172.19.199.6] (unknown [10.16.107.18]) by lists.libvirt.org (Postfix) with ESMTP id 7917E41942; Thu, 16 Apr 2026 12:26:00 -0400 (EDT) Received: by lists.libvirt.org (Postfix, from userid 993) id 552BC3F304; Thu, 16 Apr 2026 12:25:44 -0400 (EDT) Received: from mx0a-00069f02.pphosted.com (mx0a-00069f02.pphosted.com [205.220.165.32]) (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 468113F308 for ; Thu, 16 Apr 2026 12:25:41 -0400 (EDT) Received: from pps.filterd (m0246629.ppops.net [127.0.0.1]) by mx0b-00069f02.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 63GFJgEL1346694 for ; Thu, 16 Apr 2026 16:25:40 GMT Received: from phxpaimrmta01.imrmtpd1.prodappphxaev1.oraclevcn.com (phxpaimrmta01.appoci.oracle.com [138.1.114.2]) by mx0b-00069f02.pphosted.com (PPS) with ESMTPS id 4dh87h7vg8-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK) for ; Thu, 16 Apr 2026 16:25:40 +0000 (GMT) Received: from pps.filterd (phxpaimrmta01.imrmtpd1.prodappphxaev1.oraclevcn.com [127.0.0.1]) by phxpaimrmta01.imrmtpd1.prodappphxaev1.oraclevcn.com (8.18.1.7/8.18.1.7) with ESMTP id 63GGJNYv000433 for ; Thu, 16 Apr 2026 16:25:39 GMT Received: from pps.reinject (localhost [127.0.0.1]) by phxpaimrmta01.imrmtpd1.prodappphxaev1.oraclevcn.com (PPS) with ESMTPS id 4dhyk1sfsm-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK) for ; Thu, 16 Apr 2026 16:25:39 +0000 (GMT) Received: from phxpaimrmta01.imrmtpd1.prodappphxaev1.oraclevcn.com (phxpaimrmta01.imrmtpd1.prodappphxaev1.oraclevcn.com [127.0.0.1]) by pps.reinject (8.18.1.12/8.18.1.12) with ESMTP id 63GGNrtW021563 for ; Thu, 16 Apr 2026 16:25:39 GMT Received: from akulhall-vm1-ol9.osdevelopmeniad.oraclevcn.com (akulhall-vm1-ol9.allregionaliads.osdevelopmeniad.oraclevcn.com [100.100.255.191]) by phxpaimrmta01.imrmtpd1.prodappphxaev1.oraclevcn.com (PPS) with ESMTP id 4dhyk1sfpn-2; Thu, 16 Apr 2026 16:25:38 +0000 (GMT) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-26) on lists.libvirt.org X-Spam-Level: X-Spam-Status: No, score=-3.4 required=5.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_DNSWL_LOW,RCVD_IN_MSPIKE_H4, RCVD_IN_MSPIKE_WL,RCVD_IN_VALIDITY_CERTIFIED_BLOCKED, RCVD_IN_VALIDITY_RPBL_BLOCKED,RCVD_IN_VALIDITY_SAFE_BLOCKED, SPF_HELO_NONE autolearn=unavailable autolearn_force=no version=4.0.1 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oracle.com; h=cc :content-transfer-encoding:date:from:in-reply-to:message-id :mime-version:references:subject:to; s=corp-2025-04-25; bh=MqnRF PQU5e6Llp95YhrMt1RlqPiyhDUNzlnKzMjT8ms=; b=JASOjjqBNsBVtu50nt2p0 Wef8eXOqxTYsDNl89SSWOEsosllLubkKhQeQ8AK5Iq+QoXzzFRg6bwyYaBhVjvJf uwvZm77iZm1OesMMASp6z1Q8R4DUk+RxooK76WyZiO6VZMoarzlrs9Qw0qkKSCco yAi6OQ+QpmLGn4ndFsnxi0As2EHgdNNT0pciDtD+QwxUcveBkjRKJCm9nS3z8Adz hFHOj+ujn5HshhmlsEpJdzT5yIy1taitZB8n8NQZ1UugQr/KmNuGjMMVJzSuPNQa wbSixojiQfm8J6M8sjuEYSpujA6ySzVnEKa7Q7oksM9AtNDlFS8m6uH5GjE9fv40 A== To: devel@lists.libvirt.org Subject: [PATCH 1/1] qemu: support VIR_DOMAIN_VCPU_ASYNC for live vCPU unplug Date: Thu, 16 Apr 2026 21:51:23 +0530 Message-ID: <20260416162506.251888-2-akash.kulhalli@oracle.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260416162506.251888-1-akash.kulhalli@oracle.com> References: <20260416162506.251888-1-akash.kulhalli@oracle.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1143,Hydra:6.1.51,FMLib:17.12.100.49 definitions=2026-04-16_03,2026-04-16_03,2025-10-01_01 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 adultscore=0 suspectscore=0 malwarescore=0 mlxscore=0 spamscore=0 bulkscore=0 mlxlogscore=999 lowpriorityscore=0 phishscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.19.0-2604070000 definitions=main-2604160156 X-Authority-Analysis: v=2.4 cv=eJUjSnp1 c=1 sm=1 tr=0 ts=69e10d84 cx=c_pps a=XiAAW1AwiKB2Y8Wsi+sD2Q==:117 a=XiAAW1AwiKB2Y8Wsi+sD2Q==:17 a=A5OVakUREuEA:10 a=VkNPw1HP01LnGYTKEx00:22 a=jiCTI4zE5U7BLdzWsZGv:22 a=EIcjfB9IiI4px24ztqRk:22 a=yPCof4ZbAAAA:8 a=DOnILDiAerKvL8nyru4A:9 X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwNDE2MDE1NyBTYWx0ZWRfXwygaAi3wLgfU gKyl3dZz4lt0mlyyKTSR6EpW+b7O47yzpW/bb8nnWrIy5iUoPw+Mx9F1TefzyH/EmQUt+rxdro1 cXaaCgyQY2ZhPwtibdBjl0Vzx/HdzxxjFCqy50kflmyKN7B+NCBw/GlgptNcWkUFzqOiu43hq2U Y72aJjz7uXZkY9PjbK7RYIjJM6qpXLn/D4FIvhjwPGGKf20Qckuu1JtBum27Ce9EdReHP3GVZkc 73KIVvoafgcfPhf7cEjfLFHs4d+cnIpiCD0AqF3u92T2ZFYbOHJ7KzFvvUmiphL8qq+VEAFfS0X R47cForzpkV8yHCgpwBHCAmYHiR9I5Em3Csp9xIxDgeMjAn7ERZwbjfSZwvO+Tjb9B+WqNhEOsz r8JZqjWj9QI9HTX3nzXGwezpNLfyBEI35YUQAViE2ibAEEbqpmJFVPxlOTCwN/W0CKWQI0qXKCg p8PH0dqAG5su2YIF0SQ== X-Proofpoint-ORIG-GUID: _MLegFJ2sQPTIjMxqSVi1fFFzQ94o90v X-Proofpoint-GUID: _MLegFJ2sQPTIjMxqSVi1fFFzQ94o90v Message-ID-Hash: B2V22JTLVGJIN4Q5IOEUUQJY3YT3BDPD X-Message-ID-Hash: B2V22JTLVGJIN4Q5IOEUUQJY3YT3BDPD X-MailFrom: akash.kulhalli@oracle.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 CC: akash.kulhalli@oracle.com 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: Akash Kulhalli via Devel Reply-To: Akash Kulhalli X-ZohoMail-DKIM: fail (Header signature does not verify) X-ZM-MESSAGEID: 1776357073013158501 Content-Type: text/plain; charset="utf-8" Add VIR_DOMAIN_VCPU_ASYNC to the vCPU management APIs and introduce VIR_DOMAIN_EVENT_ID_VCPU_REMOVED, carrying the libvirt XML vCPU id. For live vCPU unplug, async mode returns after successfully submitting the unplug request instead of doing the short wait used by the non-async path. The live XML is left unchanged until completion is confirmed, and the final outcome is reported through domain events: successful completion emits VCPU_REMOVED, while guest-rejected unplug requests continue to emit DEVICE_REMOVAL_FAILED. This closes the current gap where successful vCPU hot-unplug emits no domain event for virsh event or other libvirt event consumers to observe. Thread the new event through the remote protocol, add --async to virsh setvcpus and virsh setvcpu, and teach virsh event and event-test about vcpu-removed. Async mode is supported only for live vCPU unplug. Signed-off-by: Akash Kulhalli --- examples/c/misc/event-test.c | 12 +++++ include/libvirt/libvirt-domain.h | 23 +++++++++ src/conf/domain_event.c | 66 +++++++++++++++++++++++++ src/conf/domain_event.h | 6 +++ src/libvirt-domain.c | 40 +++++++++++++++- src/libvirt_private.syms | 2 + src/qemu/qemu_driver.c | 33 +++++++++++-- src/qemu/qemu_hotplug.c | 74 +++++++++++++++++++++++++---- src/qemu/qemu_hotplug.h | 8 ++-- src/remote/remote_daemon_dispatch.c | 26 ++++++++++ src/remote/remote_driver.c | 29 +++++++++++ src/remote/remote_protocol.x | 14 +++++- src/remote_protocol-structs | 6 +++ tests/qemuhotplugtest.c | 6 ++- tools/virsh-domain-event.c | 16 +++++++ tools/virsh-domain.c | 34 +++++++++++++ 16 files changed, 373 insertions(+), 22 deletions(-) diff --git a/examples/c/misc/event-test.c b/examples/c/misc/event-test.c index 2ce82ca9e088..f9e65c55f064 100644 --- a/examples/c/misc/event-test.c +++ b/examples/c/misc/event-test.c @@ -1039,6 +1039,17 @@ myDomainEventDeviceRemovalFailedCallback(virConnectP= tr conn G_GNUC_UNUSED, return 0; } =20 +static int +myDomainEventVcpuRemovedCallback(virConnectPtr conn G_GNUC_UNUSED, + virDomainPtr dom, + unsigned int vcpuid, + void *opaque G_GNUC_UNUSED) +{ + printf("%s EVENT: Domain %s(%d) vcpu removed: %u\n", + __func__, virDomainGetName(dom), virDomainGetID(dom), vcpuid); + return 0; +} + =20 static const char * metadataTypeToStr(int status) @@ -1183,6 +1194,7 @@ struct domainEventData domainEvents[] =3D { DOMAIN_EVENT(VIR_DOMAIN_EVENT_ID_MEMORY_FAILURE, myDomainEventMemoryFa= ilureCallback), DOMAIN_EVENT(VIR_DOMAIN_EVENT_ID_MEMORY_DEVICE_SIZE_CHANGE, myDomainEv= entMemoryDeviceSizeChangeCallback), DOMAIN_EVENT(VIR_DOMAIN_EVENT_ID_NIC_MAC_CHANGE, myDomainEventNICMACCh= angeCallback), + DOMAIN_EVENT(VIR_DOMAIN_EVENT_ID_VCPU_REMOVED, myDomainEventVcpuRemove= dCallback), }; =20 struct storagePoolEventData { diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-dom= ain.h index 4a8e3114b35d..a326d133ee41 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -2605,6 +2605,7 @@ typedef enum { VIR_DOMAIN_VCPU_MAXIMUM =3D (1 << 2), /* Max rather than current count= (Since: 0.8.5) */ VIR_DOMAIN_VCPU_GUEST =3D (1 << 3), /* Modify state of the cpu in th= e guest (Since: 1.1.0) */ VIR_DOMAIN_VCPU_HOTPLUGGABLE =3D (1 << 4), /* Make vcpus added hot(un)= pluggable (Since: 2.4.0) */ + VIR_DOMAIN_VCPU_ASYNC =3D (1 << 5), /* Return after firing live unplug= request(s) (Since: 12.3.0) */ } virDomainVcpuFlags; =20 int virDomainSetVcpus (virDomainPtr domain, @@ -6965,6 +6966,27 @@ typedef void (*virConnectDomainEventDeviceRemovalFai= ledCallback)(virConnectPtr c const cha= r *devAlias, void *opa= que); =20 +/** + * virConnectDomainEventVcpuRemovedCallback: + * @conn: connection object + * @dom: domain on which the event occurred + * @vcpuid: libvirt XML vCPU id + * @opaque: application specified data + * + * This callback occurs when a vCPU is removed from the domain. + * + * The @vcpuid value matches the ```` value from the doma= in XML. + * + * The callback signature to use when registering for an event of type + * VIR_DOMAIN_EVENT_ID_VCPU_REMOVED with virConnectDomainEventRegisterAny(= ). + * + * Since: 12.3.0 + */ +typedef void (*virConnectDomainEventVcpuRemovedCallback)(virConnectPtr con= n, + virDomainPtr dom, + unsigned int vcpu= id, + void *opaque); + /** * virConnectDomainEventMetadataChangeCallback: * @conn: connection object @@ -7617,6 +7639,7 @@ typedef enum { VIR_DOMAIN_EVENT_ID_MEMORY_FAILURE =3D 25, /* virConnectDomainEventMe= moryFailureCallback (Since: 6.9.0) */ VIR_DOMAIN_EVENT_ID_MEMORY_DEVICE_SIZE_CHANGE =3D 26, /* virConnectDom= ainEventMemoryDeviceSizeChangeCallback (Since: 7.9.0) */ VIR_DOMAIN_EVENT_ID_NIC_MAC_CHANGE =3D 27, /* virConnectDomainEventNIC= MACChangeCallback (Since: 11.2.0) */ + VIR_DOMAIN_EVENT_ID_VCPU_REMOVED =3D 28, /* virConnectDomainEventVcpuR= emovedCallback (Since: 12.3.0) */ =20 # ifdef VIR_ENUM_SENTINELS VIR_DOMAIN_EVENT_ID_LAST diff --git a/src/conf/domain_event.c b/src/conf/domain_event.c index 88087bad4f21..17ad4a0d2c91 100644 --- a/src/conf/domain_event.c +++ b/src/conf/domain_event.c @@ -53,6 +53,7 @@ static virClass *virDomainEventDeviceAddedClass; static virClass *virDomainEventMigrationIterationClass; static virClass *virDomainEventJobCompletedClass; static virClass *virDomainEventDeviceRemovalFailedClass; +static virClass *virDomainEventVcpuRemovedClass; static virClass *virDomainEventMetadataChangeClass; static virClass *virDomainEventBlockThresholdClass; static virClass *virDomainEventMemoryFailureClass; @@ -78,6 +79,7 @@ static void virDomainEventDeviceAddedDispose(void *obj); static void virDomainEventMigrationIterationDispose(void *obj); static void virDomainEventJobCompletedDispose(void *obj); static void virDomainEventDeviceRemovalFailedDispose(void *obj); +static void virDomainEventVcpuRemovedDispose(void *obj); static void virDomainEventMetadataChangeDispose(void *obj); static void virDomainEventBlockThresholdDispose(void *obj); static void virDomainEventMemoryFailureDispose(void *obj); @@ -251,6 +253,13 @@ struct _virDomainEventDeviceRemovalFailed { }; typedef struct _virDomainEventDeviceRemovalFailed virDomainEventDeviceRemo= valFailed; =20 +struct _virDomainEventVcpuRemoved { + virDomainEvent parent; + + unsigned int vcpuid; +}; +typedef struct _virDomainEventVcpuRemoved virDomainEventVcpuRemoved; + struct _virDomainEventMetadataChange { virDomainEvent parent; =20 @@ -337,6 +346,8 @@ virDomainEventsOnceInit(void) return -1; if (!VIR_CLASS_NEW(virDomainEventDeviceRemovalFailed, virDomainEventCl= ass)) return -1; + if (!VIR_CLASS_NEW(virDomainEventVcpuRemoved, virDomainEventClass)) + return -1; if (!VIR_CLASS_NEW(virDomainEventMetadataChange, virDomainEventClass)) return -1; if (!VIR_CLASS_NEW(virDomainEventBlockThreshold, virDomainEventClass)) @@ -484,6 +495,13 @@ virDomainEventDeviceRemovalFailedDispose(void *obj) g_free(event->devAlias); } =20 +static void +virDomainEventVcpuRemovedDispose(void *obj) +{ + virDomainEventVcpuRemoved *event =3D obj; + VIR_DEBUG("obj=3D%p", event); +} + =20 static void virDomainEventPMDispose(void *obj) @@ -1382,6 +1400,43 @@ virDomainEventDeviceRemovalFailedNewFromDom(virDomai= nPtr dom, devAlias); } =20 +static virObjectEvent * +virDomainEventVcpuRemovedNew(int id, + const char *name, + unsigned char *uuid, + unsigned int vcpuid) +{ + virDomainEventVcpuRemoved *ev; + + if (virDomainEventsInitialize() < 0) + return NULL; + + if (!(ev =3D virDomainEventNew(virDomainEventVcpuRemovedClass, + VIR_DOMAIN_EVENT_ID_VCPU_REMOVED, + id, name, uuid))) + return NULL; + + ev->vcpuid =3D vcpuid; + + return (virObjectEvent *)ev; +} + +virObjectEvent * +virDomainEventVcpuRemovedNewFromObj(virDomainObj *obj, + unsigned int vcpuid) +{ + return virDomainEventVcpuRemovedNew(obj->def->id, obj->def->name, + obj->def->uuid, vcpuid); +} + +virObjectEvent * +virDomainEventVcpuRemovedNewFromDom(virDomainPtr dom, + unsigned int vcpuid) +{ + return virDomainEventVcpuRemovedNew(dom->id, dom->name, dom->uuid, + vcpuid); +} + =20 static virObjectEvent * virDomainEventAgentLifecycleNew(int id, @@ -2134,6 +2189,17 @@ virDomainEventDispatchDefaultFunc(virConnectPtr conn, goto cleanup; } =20 + case VIR_DOMAIN_EVENT_ID_VCPU_REMOVED: + { + virDomainEventVcpuRemoved *vcpuRemovedEvent; + + vcpuRemovedEvent =3D (virDomainEventVcpuRemoved *)event; + ((virConnectDomainEventVcpuRemovedCallback)cb)(conn, dom, + vcpuRemovedEven= t->vcpuid, + cbopaque); + goto cleanup; + } + case VIR_DOMAIN_EVENT_ID_LAST: break; } diff --git a/src/conf/domain_event.h b/src/conf/domain_event.h index f31cfb9e42ad..1b1b16095a77 100644 --- a/src/conf/domain_event.h +++ b/src/conf/domain_event.h @@ -192,6 +192,12 @@ virDomainEventDeviceRemovalFailedNewFromObj(virDomainO= bj *obj, virObjectEvent * virDomainEventDeviceRemovalFailedNewFromDom(virDomainPtr dom, const char *devAlias); +virObjectEvent * +virDomainEventVcpuRemovedNewFromObj(virDomainObj *obj, + unsigned int vcpuid); +virObjectEvent * +virDomainEventVcpuRemovedNewFromDom(virDomainPtr dom, + unsigned int vcpuid); =20 virObjectEvent * virDomainEventTunableNewFromObj(virDomainObj *obj, diff --git a/src/libvirt-domain.c b/src/libvirt-domain.c index db9eea57745c..c290dc6efeca 100644 --- a/src/libvirt-domain.c +++ b/src/libvirt-domain.c @@ -7702,6 +7702,14 @@ virDomainSendProcessSignal(virDomainPtr domain, * whether it also affects persistent configuration; for more control, * use virDomainSetVcpusFlags(). * + * When this API decreases the live vCPU count by hot-unplugging vCPUs in + * the hypervisor, completion may be asynchronous. Successful unplug + * completion is reported by VIR_DOMAIN_EVENT_ID_VCPU_REMOVED, carrying the + * XML ```` value. Rejected unplug requests continue to be + * reported by VIR_DOMAIN_EVENT_ID_DEVICE_REMOVAL_FAILED. The success event + * may be delivered before this API call returns. A timeout is reported on= ly + * through the returned error, not through a domain event. + * * Returns 0 in case of success, -1 in case of failure. * * Since: 0.1.4 @@ -7773,8 +7781,23 @@ virDomainSetVcpus(virDomainPtr domain, unsigned int = nvcpus) * be used with live guests and is incompatible with VIR_DOMAIN_VCPU_MAXIM= UM. * The usage of this flag may require a guest agent configured. * + * If @flags includes VIR_DOMAIN_VCPU_ASYNC, only vCPU hot-unplug is reque= sted + * asynchronously. In this mode, success means that all required unplug + * request(s) were successfully fired; final completion is reported by + * the VIR_DOMAIN_EVENT_ID_VCPU_REMOVED event, and rejection is reported b= y a + * `VIR_DOMAIN_EVENT_ID_DEVICE_REMOVAL_FAILED` event type. + * * Not all hypervisors can support all flag combinations. * + * When this API decreases the live vCPU count by hot-unplugging vCPUs, + * completion may be asynchronous. Successful unplug completion is reporte= d by + * VIR_DOMAIN_EVENT_ID_VCPU_REMOVED, carrying the XML ```` + * value within the event data. Rejected unplug requests continue to be + * reported by VIR_DOMAIN_EVENT_ID_DEVICE_REMOVAL_FAILED. The success event + * may be delivered before this API call returns. In non-async mode, a + * timeout is reported only through the returned error, not through a doma= in + * event. + * * Returns 0 in case of success, -1 in case of failure. * * Since: 0.8.5 @@ -13125,7 +13148,8 @@ virDomainSetGuestVcpus(virDomainPtr domain, * @domain: pointer to domain object * @vcpumap: text representation of a bitmap of vcpus to set * @state: 0 to disable/1 to enable cpus described by @vcpumap - * @flags: bitwise-OR of virDomainModificationImpact + * @flags: bitwise-OR of virDomainModificationImpact with optional + * VIR_DOMAIN_VCPU_ASYNC * * Enables/disables individual vcpus described by @vcpumap in the hypervis= or. * @@ -13134,6 +13158,20 @@ virDomainSetGuestVcpus(virDomainPtr domain, * * Note that OSes and hypervisors may require vCPU 0 to stay online. * + * If @flags includes VIR_DOMAIN_VCPU_ASYNC, only live vCPU disable + * (hot-unplug) is requested asynchronously. In this mode, success means t= he + * unplug request was successfully fired; final completion is reported by + * VIR_DOMAIN_EVENT_ID_VCPU_REMOVED, while rejection is reported by + * VIR_DOMAIN_EVENT_ID_DEVICE_REMOVAL_FAILED. + * + * When this API disables live vCPUs by hot-unplugging them, the operation + * completion may be asynchronous. Successful unplug completion is reporte= d by + * VIR_DOMAIN_EVENT_ID_VCPU_REMOVED, carrying the XML ```` + * value. Rejected unplug requests continue to be reported by + * VIR_DOMAIN_EVENT_ID_DEVICE_REMOVAL_FAILED. The success event may be + * delivered before this API call returns. In non-async mode, a timeout is + * reported only through the returned error, not through a domain event. + * * Returns 0 on success, -1 on error. * * Since: 3.1.0 diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index cf0e71cc6af1..f22b5895db12 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -811,6 +811,8 @@ virDomainEventTrayChangeNewFromDom; virDomainEventTrayChangeNewFromObj; virDomainEventTunableNewFromDom; virDomainEventTunableNewFromObj; +virDomainEventVcpuRemovedNewFromDom; +virDomainEventVcpuRemovedNewFromObj; virDomainEventWatchdogNewFromDom; virDomainEventWatchdogNewFromObj; virDomainQemuMonitorEventNew; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index d227ac58cdb4..ad4dc11c970f 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -3620,7 +3620,14 @@ processDeviceDeletedEvent(virQEMUDriver *driver, } =20 if (STRPREFIX(devAlias, "vcpu")) { - qemuDomainRemoveVcpuAlias(vm, devAlias); + int vcpuid; + virObjectEvent *event; + if ((vcpuid =3D qemuDomainRemoveVcpuAlias(vm, devAlias)) =3D=3D -1) + goto endjob; + + event =3D virDomainEventVcpuRemovedNewFromObj(vm, vcpuid); + virObjectEventStateQueue(driver->domainEventState, event); + } else { if (virDomainDefFindDevice(vm->def, devAlias, &dev, true) < 0) goto endjob; @@ -4269,6 +4276,7 @@ qemuDomainSetVcpusFlags(virDomainPtr dom, virDomainObj *vm =3D NULL; virDomainDef *def; virDomainDef *persistentDef; + bool async =3D !!(flags & VIR_DOMAIN_VCPU_ASYNC); bool hotpluggable =3D !!(flags & VIR_DOMAIN_VCPU_HOTPLUGGABLE); bool useAgent =3D !!(flags & VIR_DOMAIN_VCPU_GUEST); int ret =3D -1; @@ -4277,7 +4285,8 @@ qemuDomainSetVcpusFlags(virDomainPtr dom, VIR_DOMAIN_AFFECT_CONFIG | VIR_DOMAIN_VCPU_MAXIMUM | VIR_DOMAIN_VCPU_GUEST | - VIR_DOMAIN_VCPU_HOTPLUGGABLE, -1); + VIR_DOMAIN_VCPU_HOTPLUGGABLE | + VIR_DOMAIN_VCPU_ASYNC, -1); =20 if (!(vm =3D qemuDomainObjFromDomain(dom))) goto cleanup; @@ -4297,13 +4306,19 @@ qemuDomainSetVcpusFlags(virDomainPtr dom, if (virDomainObjGetDefs(vm, flags, &def, &persistentDef) < 0) goto endjob; =20 + if (async && (useAgent || persistentDef || !def)) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("asynchronous mode is supported only for live vcp= u unplug")); + goto endjob; + } + if (useAgent) ret =3D qemuDomainSetVcpusAgent(vm, nvcpus); else if (flags & VIR_DOMAIN_VCPU_MAXIMUM) ret =3D qemuDomainSetVcpusMax(driver, vm, def, persistentDef, nvcp= us); else ret =3D qemuDomainSetVcpusInternal(driver, vm, def, persistentDef, - nvcpus, hotpluggable); + nvcpus, hotpluggable, async); =20 endjob: if (useAgent) @@ -19169,12 +19184,14 @@ qemuDomainSetVcpu(virDomainPtr dom, virDomainObj *vm =3D NULL; virDomainDef *def =3D NULL; virDomainDef *persistentDef =3D NULL; + bool async =3D !!(flags & VIR_DOMAIN_VCPU_ASYNC); g_autoptr(virBitmap) map =3D NULL; ssize_t lastvcpu; int ret =3D -1; =20 virCheckFlags(VIR_DOMAIN_AFFECT_LIVE | - VIR_DOMAIN_AFFECT_CONFIG, -1); + VIR_DOMAIN_AFFECT_CONFIG | + VIR_DOMAIN_VCPU_ASYNC, -1); =20 if (state !=3D 0 && state !=3D 1) { virReportInvalidArg(state, "%s", _("unsupported state value")); @@ -19220,7 +19237,13 @@ qemuDomainSetVcpu(virDomainPtr dom, } } =20 - ret =3D qemuDomainSetVcpuInternal(driver, vm, def, persistentDef, map,= !!state); + if (async && (persistentDef || !def)) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("asynchronous mode is supported only for live vcp= u unplug")); + goto endjob; + } + + ret =3D qemuDomainSetVcpuInternal(driver, vm, def, persistentDef, map,= !!state, async); =20 endjob: virDomainObjEndJob(vm); diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index b7a282b96e52..0b3a781cea19 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -5723,6 +5723,12 @@ qemuDomainRemoveDevice(virQEMUDriver *driver, return 0; } =20 +static bool +qemuDomainDeviceRemoved(virDomainObj *vm) +{ + qemuDomainObjPrivate *priv =3D vm->privateData; + return priv->unplug.status =3D=3D QEMU_DOMAIN_UNPLUGGING_DEVICE_STATUS= _OK; +} =20 static void qemuDomainMarkDeviceAliasForRemoval(virDomainObj *vm, @@ -6761,7 +6767,7 @@ qemuDomainRemoveVcpu(virDomainObj *vm, } =20 =20 -void +int qemuDomainRemoveVcpuAlias(virDomainObj *vm, const char *alias) { @@ -6774,13 +6780,16 @@ qemuDomainRemoveVcpuAlias(virDomainObj *vm, vcpupriv =3D QEMU_DOMAIN_VCPU_PRIVATE(vcpu); =20 if (STREQ_NULLABLE(alias, vcpupriv->alias)) { - qemuDomainRemoveVcpu(vm, i); - return; + if (qemuDomainRemoveVcpu(vm, i) < 0) + return -1; + + return i; } } =20 VIR_DEBUG("vcpu '%s' not found in vcpulist of domain '%s'", alias, vm->def->name); + return -1; } =20 =20 @@ -6788,7 +6797,8 @@ static int qemuDomainHotplugDelVcpu(virQEMUDriver *driver, virQEMUDriverConfig *cfg, virDomainObj *vm, - unsigned int vcpu) + unsigned int vcpu, + bool async) { virDomainVcpuDef *vcpuinfo =3D virDomainDefGetVcpu(vm->def, vcpu); qemuDomainVcpuPrivate *vcpupriv =3D QEMU_DOMAIN_VCPU_PRIVATE(vcpuinfo); @@ -6796,6 +6806,7 @@ qemuDomainHotplugDelVcpu(virQEMUDriver *driver, unsigned int nvcpus =3D vcpupriv->vcpus; int rc; int ret =3D -1; + virObjectEvent *event =3D NULL; =20 if (!vcpupriv->alias) { virReportError(VIR_ERR_OPERATION_UNSUPPORTED, @@ -6813,6 +6824,22 @@ qemuDomainHotplugDelVcpu(virQEMUDriver *driver, goto cleanup; } } else { + if (async) { + /* rc =3D 0 is implied in this branch */ + if (qemuDomainDeviceRemoved(vm)) { + /* event has already arrived, handle it now */ + goto success; + } + /* + * event has not arrived yet, but the monitor operation was + * successful; there will not be a waiter anymore when this th= read + * exits. Reset removal state now so that the event handling p= ath + * can be properly triggered if and when the event does arrive + */ + ret =3D 0; + goto cleanup; + } + if ((rc =3D qemuDomainWaitForDeviceRemoval(vm)) <=3D 0) { if (rc =3D=3D 0) virReportError(VIR_ERR_OPERATION_TIMEOUT, "%s", @@ -6821,6 +6848,7 @@ qemuDomainHotplugDelVcpu(virQEMUDriver *driver, } } =20 + success: if (qemuDomainRemoveVcpu(vm, vcpu) < 0) goto cleanup; =20 @@ -6831,6 +6859,10 @@ qemuDomainHotplugDelVcpu(virQEMUDriver *driver, =20 ret =3D 0; =20 + /* emit event now to close the async caller loop */ + event =3D virDomainEventVcpuRemovedNewFromObj(vm, vcpu); + virObjectEventStateQueue(driver->domainEventState, event); + cleanup: qemuDomainResetDeviceRemoval(vm); return ret; @@ -7003,7 +7035,8 @@ qemuDomainSetVcpusLive(virQEMUDriver *driver, virQEMUDriverConfig *cfg, virDomainObj *vm, virBitmap *vcpumap, - bool enable) + bool enable, + bool async) { qemuDomainObjPrivate *priv =3D vm->privateData; virCgroupEmulatorAllNodesData *emulatorCgroup =3D NULL; @@ -7023,7 +7056,7 @@ qemuDomainSetVcpusLive(virQEMUDriver *driver, if (!virBitmapIsBitSet(vcpumap, nextvcpu)) continue; =20 - if (qemuDomainHotplugDelVcpu(driver, cfg, vm, nextvcpu) < 0) + if (qemuDomainHotplugDelVcpu(driver, cfg, vm, nextvcpu, async)= < 0) goto cleanup; } } @@ -7114,12 +7147,20 @@ qemuDomainSetVcpusInternal(virQEMUDriver *driver, virDomainDef *def, virDomainDef *persistentDef, unsigned int nvcpus, - bool hotpluggable) + bool hotpluggable, + bool async) { g_autoptr(virQEMUDriverConfig) cfg =3D virQEMUDriverGetConfig(driver); g_autoptr(virBitmap) vcpumap =3D NULL; bool enable; =20 + if (async && persistentDef) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("asynchronous mode is supported only for live vcpu unplug")); + + return -1; + } + if (def && nvcpus > virDomainDefGetVcpusMax(def)) { virReportError(VIR_ERR_INVALID_ARG, _("requested vcpus is greater than max allowable vc= pus for the live domain: %1$u > %2$u"), @@ -7139,7 +7180,13 @@ qemuDomainSetVcpusInternal(virQEMUDriver *driver, &enable))) return -1; =20 - if (qemuDomainSetVcpusLive(driver, cfg, vm, vcpumap, enable) < 0) + if (async && enable) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("asynchronous mode is supported only for vcpu= unplug")); + return -1; + } + + if (qemuDomainSetVcpusLive(driver, cfg, vm, vcpumap, enable, async= ) < 0) return -1; } =20 @@ -7289,7 +7336,8 @@ qemuDomainSetVcpuInternal(virQEMUDriver *driver, virDomainDef *def, virDomainDef *persistentDef, virBitmap *map, - bool state) + bool state, + bool async) { g_autoptr(virQEMUDriverConfig) cfg =3D virQEMUDriverGetConfig(driver); g_autoptr(virBitmap) livevcpus =3D NULL; @@ -7320,8 +7368,14 @@ qemuDomainSetVcpuInternal(virQEMUDriver *driver, return -1; } =20 + if (async && state) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("asynchronous mode is supported only for vcpu unp= lug")); + return -1; + } + if (livevcpus && - qemuDomainSetVcpusLive(driver, cfg, vm, livevcpus, state) < 0) + qemuDomainSetVcpusLive(driver, cfg, vm, livevcpus, state, async) <= 0) return -1; =20 if (persistentDef) { diff --git a/src/qemu/qemu_hotplug.h b/src/qemu/qemu_hotplug.h index e6c90253e416..aa2a9ea6eb75 100644 --- a/src/qemu/qemu_hotplug.h +++ b/src/qemu/qemu_hotplug.h @@ -79,7 +79,7 @@ qemuDomainUpdateDeviceLive(virDomainObj *vm, virQEMUDriver *driver, bool force); =20 -void +int qemuDomainRemoveVcpuAlias(virDomainObj *vm, const char *alias); =20 @@ -106,7 +106,8 @@ qemuDomainSetVcpusInternal(virQEMUDriver *driver, virDomainDef *def, virDomainDef *persistentDef, unsigned int nvcpus, - bool hotpluggable); + bool hotpluggable, + bool async); =20 int qemuDomainSetVcpuInternal(virQEMUDriver *driver, @@ -114,7 +115,8 @@ qemuDomainSetVcpuInternal(virQEMUDriver *driver, virDomainDef *def, virDomainDef *persistentDef, virBitmap *vcpus, - bool state); + bool state, + bool async); =20 unsigned long long qemuDomainGetUnplugTimeout(virDomainObj *vm) ATTRIBUTE_MOCKABLE; diff --git a/src/remote/remote_daemon_dispatch.c b/src/remote/remote_daemon= _dispatch.c index 7e74ff063f5b..81b0ed00da1a 100644 --- a/src/remote/remote_daemon_dispatch.c +++ b/src/remote/remote_daemon_dispatch.c @@ -1322,6 +1322,31 @@ remoteRelayDomainEventMemoryDeviceSizeChange(virConn= ectPtr conn, return 0; } =20 +static int +remoteRelayDomainEventVcpuRemoved(virConnectPtr conn, + virDomainPtr dom, + unsigned int vcpuid, + void *opaque) +{ + daemonClientEventCallback *callback =3D opaque; + remote_domain_event_vcpu_removed_msg data; + + if (callback->callbackID < 0 || + !remoteRelayDomainEventCheckACL(callback->client, conn, dom)) + return -1; + + memset(&data, 0, sizeof(data)); + data.callbackID =3D callback->callbackID; + data.vcpuid =3D vcpuid; + make_nonnull_domain(&data.dom, dom); + + remoteDispatchObjectEventSend(callback->client, remoteProgram, + REMOTE_PROC_DOMAIN_EVENT_VCPU_REMOVED, + (xdrproc_t)xdr_remote_domain_event_vcpu_= removed_msg, + &data); + return 0; +} + =20 static int remoteRelayDomainEventNICMACChange(virConnectPtr conn, @@ -1383,6 +1408,7 @@ static virConnectDomainEventGenericCallback domainEve= ntCallbacks[] =3D { VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventMemoryFailure), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventMemoryDeviceSizeChange= ), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventNICMACChange), + VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventVcpuRemoved), }; =20 G_STATIC_ASSERT(G_N_ELEMENTS(domainEventCallbacks) =3D=3D VIR_DOMAIN_EVENT= _ID_LAST); diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index ec71eaed8762..c8a4e3f6da98 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -428,6 +428,10 @@ remoteDomainBuildEventMemoryDeviceSizeChange(virNetCli= entProgram *prog, virNetClient *client, void *evdata, void *opaque); static void +remoteDomainBuildEventVcpuRemoved(virNetClientProgram *prog, + virNetClient *client, + void *evdata, void *opaque); +static void remoteConnectNotifyEventConnectionClosed(virNetClientProgram *prog G_GNUC_= UNUSED, virNetClient *client G_GNUC_UNUSE= D, void *evdata, void *opaque); @@ -659,6 +663,10 @@ static virNetClientProgramEvent remoteEvents[] =3D { remoteDomainBuildEventNICMACChange, sizeof(remote_domain_event_nic_mac_change_msg), (xdrproc_t)xdr_remote_domain_event_nic_mac_change_msg }, + { REMOTE_PROC_DOMAIN_EVENT_VCPU_REMOVED, + remoteDomainBuildEventVcpuRemoved, + sizeof(remote_domain_event_vcpu_removed_msg), + (xdrproc_t)xdr_remote_domain_event_vcpu_removed_msg }, }; =20 static void @@ -5138,6 +5146,27 @@ remoteDomainBuildEventMemoryDeviceSizeChange(virNetC= lientProgram *prog G_GNUC_UN virObjectEventStateQueueRemote(priv->eventState, event, msg->callbackI= D); } =20 +static void +remoteDomainBuildEventVcpuRemoved(virNetClientProgram *prog G_GNUC_UNUSED, + virNetClient *client G_GNUC_UNUSED, + void *evdata, void *opaque) +{ + virConnectPtr conn =3D opaque; + remote_domain_event_vcpu_removed_msg *msg =3D evdata; + struct private_data *priv =3D conn->privateData; + virDomainPtr dom; + virObjectEvent *event =3D NULL; + + if (!(dom =3D get_nonnull_domain(conn, msg->dom))) + return; + + event =3D virDomainEventVcpuRemovedNewFromDom(dom, msg->vcpuid); + + virObjectUnref(dom); + + virObjectEventStateQueueRemote(priv->eventState, event, msg->callbackI= D); +} + =20 static void remoteDomainBuildEventNICMACChange(virNetClientProgram *prog G_GNUC_UNUSED, diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index 38a83c64eadb..23699e99a60a 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -3981,6 +3981,12 @@ struct remote_domain_event_memory_device_size_change= _msg { unsigned hyper size; }; =20 +struct remote_domain_event_vcpu_removed_msg { + int callbackID; + remote_nonnull_domain dom; + unsigned int vcpuid; +}; + =20 struct remote_domain_fd_associate_args { remote_nonnull_domain dom; @@ -7120,5 +7126,11 @@ enum remote_procedure { * @generate: both * @acl: none */ - REMOTE_PROC_DOMAIN_EVENT_NIC_MAC_CHANGE =3D 453 + REMOTE_PROC_DOMAIN_EVENT_NIC_MAC_CHANGE =3D 453, + + /** + * @generate: both + * @acl: none + */ + REMOTE_PROC_DOMAIN_EVENT_VCPU_REMOVED =3D 454 }; diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index 0f87d13a5ae1..75c3d0cb2e06 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -3315,6 +3315,11 @@ struct remote_domain_event_memory_device_size_change= _msg { remote_nonnull_string alias; uint64_t size; }; +struct remote_domain_event_vcpu_removed_msg { + int callbackID; + remote_nonnull_domain dom; + u_int vcpuid; +}; struct remote_domain_fd_associate_args { remote_nonnull_domain dom; remote_nonnull_string name; @@ -3791,4 +3796,5 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_SET_THROTTLE_GROUP =3D 451, REMOTE_PROC_DOMAIN_DEL_THROTTLE_GROUP =3D 452, REMOTE_PROC_DOMAIN_EVENT_NIC_MAC_CHANGE =3D 453, + REMOTE_PROC_DOMAIN_EVENT_VCPU_REMOVED =3D 454, }; diff --git a/tests/qemuhotplugtest.c b/tests/qemuhotplugtest.c index ea9d3243f8b1..36bc4d913826 100644 --- a/tests/qemuhotplugtest.c +++ b/tests/qemuhotplugtest.c @@ -322,6 +322,7 @@ struct testQemuHotplugCpuParams { GHashTable *capsLatestFiles; GHashTable *capsCache; GHashTable *schemaCache; + bool async; }; =20 =20 @@ -420,7 +421,7 @@ testQemuHotplugCpuGroup(const void *opaque) =20 rc =3D qemuDomainSetVcpusInternal(&driver, data->vm, data->vm->def, data->vm->newDef, params->newcpus, - true); + true, params->async); =20 if (params->fail) { if (rc =3D=3D 0) @@ -458,7 +459,8 @@ testQemuHotplugCpuIndividual(const void *opaque) goto cleanup; =20 rc =3D qemuDomainSetVcpuInternal(&driver, data->vm, data->vm->def, - data->vm->newDef, map, params->state); + data->vm->newDef, map, params->state, + params->async); =20 if (params->fail) { if (rc =3D=3D 0) diff --git a/tools/virsh-domain-event.c b/tools/virsh-domain-event.c index b9d1cdf019ca..fdd7bf611605 100644 --- a/tools/virsh-domain-event.c +++ b/tools/virsh-domain-event.c @@ -705,6 +705,20 @@ virshEventDeviceRemovalFailedPrint(virConnectPtr conn = G_GNUC_UNUSED, virshEventPrint(opaque, &buf); } =20 +static void +virshEventVcpuRemovedPrint(virConnectPtr conn G_GNUC_UNUSED, + virDomainPtr dom, + unsigned int vcpuid, + void *opaque) +{ + g_auto(virBuffer) buf =3D VIR_BUFFER_INITIALIZER; + + virBufferAsprintf(&buf, + _("event 'vcpu-removed' for domain '%1$s': vcpu: %2$= u\n"), + virDomainGetName(dom), vcpuid); + virshEventPrint(opaque, &buf); +} + VIR_ENUM_DECL(virshEventMetadataChangeType); VIR_ENUM_IMPL(virshEventMetadataChangeType, VIR_DOMAIN_METADATA_LAST, @@ -873,6 +887,8 @@ virshDomainEventCallback virshDomainEventCallbacks[] = =3D { VIR_DOMAIN_EVENT_CALLBACK(virshEventMemoryDeviceSizeChangePrint), }, { "nic-mac-change", VIR_DOMAIN_EVENT_CALLBACK(virshEventNICMACChangePrint), }, + { "vcpu-removed", + VIR_DOMAIN_EVENT_CALLBACK(virshEventVcpuRemovedPrint), }, }; G_STATIC_ASSERT(VIR_DOMAIN_EVENT_ID_LAST =3D=3D G_N_ELEMENTS(virshDomainEv= entCallbacks)); =20 diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 08a1ce395378..350a3b6cd8b2 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -7667,6 +7667,10 @@ static const vshCmdOptDef opts_setvcpus[] =3D { .type =3D VSH_OT_BOOL, .help =3D N_("make added vcpus hot(un)pluggable") }, + {.name =3D "async", + .type =3D VSH_OT_BOOL, + .help =3D N_("return after firing live vcpu unplug request(s)") + }, {.name =3D NULL} }; =20 @@ -7681,6 +7685,7 @@ cmdSetvcpus(vshControl *ctl, const vshCmd *cmd) bool current =3D vshCommandOptBool(cmd, "current"); bool guest =3D vshCommandOptBool(cmd, "guest"); bool hotpluggable =3D vshCommandOptBool(cmd, "hotpluggable"); + bool async =3D vshCommandOptBool(cmd, "async"); unsigned int flags =3D VIR_DOMAIN_AFFECT_CURRENT; =20 VSH_EXCLUSIVE_OPTIONS_VAR(current, live); @@ -7699,6 +7704,8 @@ cmdSetvcpus(vshControl *ctl, const vshCmd *cmd) flags |=3D VIR_DOMAIN_VCPU_MAXIMUM; if (hotpluggable) flags |=3D VIR_DOMAIN_VCPU_HOTPLUGGABLE; + if (async) + flags |=3D VIR_DOMAIN_VCPU_ASYNC; =20 if (!(dom =3D virshCommandOptDomain(ctl, cmd, NULL))) return false; @@ -7711,6 +7718,12 @@ cmdSetvcpus(vshControl *ctl, const vshCmd *cmd) return false; } =20 + if (async && (config || maximum || guest || hotpluggable)) { + vshError(ctl, "%s", + _("--async can be used only for live hypervisor vcpu unpl= ug")); + return false; + } + /* none of the options were specified */ if (!current && flags =3D=3D 0) { if (virDomainSetVcpus(dom, count) !=3D 0) @@ -7720,6 +7733,10 @@ cmdSetvcpus(vshControl *ctl, const vshCmd *cmd) return false; } =20 + if (async) + vshPrintExtra(ctl, "%s", + _("vCPU unplug requests sent successfully\n")); + return true; } =20 @@ -7829,6 +7846,10 @@ static const vshCmdOptDef opts_setvcpu[] =3D { .type =3D VSH_OT_BOOL, .help =3D N_("disable cpus specified by cpumap") }, + {.name =3D "async", + .type =3D VSH_OT_BOOL, + .help =3D N_("return after firing live vcpu unplug request") + }, VIRSH_COMMON_OPT_DOMAIN_CONFIG, VIRSH_COMMON_OPT_DOMAIN_LIVE, VIRSH_COMMON_OPT_DOMAIN_CURRENT, @@ -7843,6 +7864,7 @@ cmdSetvcpu(vshControl *ctl, const vshCmd *cmd) bool disable =3D vshCommandOptBool(cmd, "disable"); bool config =3D vshCommandOptBool(cmd, "config"); bool live =3D vshCommandOptBool(cmd, "live"); + bool async =3D vshCommandOptBool(cmd, "async"); const char *vcpulist =3D NULL; int state =3D 0; unsigned int flags =3D VIR_DOMAIN_AFFECT_CURRENT; @@ -7856,12 +7878,20 @@ cmdSetvcpu(vshControl *ctl, const vshCmd *cmd) flags |=3D VIR_DOMAIN_AFFECT_CONFIG; if (live) flags |=3D VIR_DOMAIN_AFFECT_LIVE; + if (async) + flags |=3D VIR_DOMAIN_VCPU_ASYNC; =20 if (!(enable || disable)) { vshError(ctl, "%s", _("one of --enable, --disable is required")); return false; } =20 + if (async && (config || enable)) { + vshError(ctl, "%s", + _("--async can be used only for live vcpu disable")); + return false; + } + if (vshCommandOptString(ctl, cmd, "vcpulist", &vcpulist)) return false; =20 @@ -7874,6 +7904,10 @@ cmdSetvcpu(vshControl *ctl, const vshCmd *cmd) if (virDomainSetVcpu(dom, vcpulist, state, flags) < 0) return false; =20 + if (async) + vshPrintExtra(ctl, "%s", + _("vCPU unplug request sent successfully\n")); + return true; } =20 --=20 2.47.3