From nobody Mon Feb 9 23:39:54 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=quarantine dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1770199737; cv=none; d=zohomail.com; s=zohoarc; b=bTPz7JPReLLBVAHNfZQM4tEWr3EFQslEwhSWi/g6OfsqW5M5NzXCblf6ftF0W3jxDE+TwoNe2Y+d0kKLMx9ghXmifr/Y+Zgl2qprpEJwr6ROI765e+2wqjYBxN6u8EORKyj6t1X6Ia6hCY+k7UWFt5BO/l+81A0H7RtcwhDwUhc= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1770199737; 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:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=Z1lH8AOUpmQNQYZiTsi4fpNFm8Ruxo3VLn/JWrOlFL8=; b=BLFJZgda1tQ1G0dJmh0bGoI3DzLBu1MTxlc+N1/5CUo2NR3/FflroCGlSVXRhkTYzaXysf56cZV4yVgy/zU0C2EAj7bgS6O/Hk0UmsbhS6tOUU+wtt51zrQ5AGtgbnC0pIDldD+kCVPB66zvToe4yE/lsa8/RUSgPpXwn+oDGGQ= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1770199737524527.8553891270766; Wed, 4 Feb 2026 02:08:57 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vnZnu-0006ad-4x; Wed, 04 Feb 2026 05:08:10 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vnZnn-0006Ws-P5 for qemu-devel@nongnu.org; Wed, 04 Feb 2026 05:08:04 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vnZni-000544-RS for qemu-devel@nongnu.org; Wed, 04 Feb 2026 05:08:02 -0500 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-88-ihuF_kPpMoeL9lqR6BkLKQ-1; Wed, 04 Feb 2026 05:07:52 -0500 Received: from mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.93]) (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 133C118005AD; Wed, 4 Feb 2026 10:07:51 +0000 (UTC) Received: from localhost (unknown [10.45.242.6]) by mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 2616B18004AD; Wed, 4 Feb 2026 10:07:47 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1770199678; 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=Z1lH8AOUpmQNQYZiTsi4fpNFm8Ruxo3VLn/JWrOlFL8=; b=JGsdloeXlxaHo7ZW4vF3+4tgVmy8hQfbnve1yQaQvkIQC+C3mqaGJVfySojpxf/eGA1GR1 L/Xsplx+OmYzGUMMU228KrN8X36fqF1akRBgMlB8uIAsqTSMy6MdTBgll/NL1/FauYRPxV wIRfnrapfjPVI+uaOoPBXBGePoEvq+U= X-MC-Unique: ihuF_kPpMoeL9lqR6BkLKQ-1 X-Mimecast-MFC-AGG-ID: ihuF_kPpMoeL9lqR6BkLKQ_1770199671 From: marcandre.lureau@redhat.com To: qemu-devel@nongnu.org Cc: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= , kvm@vger.kernel.org, Alex Williamson , Paolo Bonzini , Ben Chaney , "Michael S. Tsirkin" , David Hildenbrand , Fabiano Rosas , Peter Xu , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= , Mark Kanda , =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Subject: [PATCH 06/10] system/memory: split RamDiscardManager into source and manager Date: Wed, 4 Feb 2026 14:07:02 +0400 Message-ID: <20260204100708.724800-7-marcandre.lureau@redhat.com> In-Reply-To: <20260204100708.724800-1-marcandre.lureau@redhat.com> References: <20260204100708.724800-1-marcandre.lureau@redhat.com> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.93 Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=170.10.133.124; envelope-from=marcandre.lureau@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H2=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1770199740245154100 From: Marc-Andr=C3=A9 Lureau Refactor the RamDiscardManager interface into two distinct components: - RamDiscardSource: An interface that state providers (virtio-mem, RamBlockAttributes) implement to provide discard state information (granularity, populated/discarded ranges, replay callbacks). - RamDiscardManager: A concrete QOM object that wraps a source, owns the listener list, and handles listener registration/unregistration and notifications. This separation moves the listener management logic from individual source implementations into the central RamDiscardManager, reducing code duplication between virtio-mem and RamBlockAttributes. The change prepares for future work where a RamDiscardManager could aggregate multiple sources. Note, the original virtio-mem code had conditions before discard: if (vmem->size) { rdl->notify_discard(rdl, rdl->section); } however, the new code calls discard unconditionally. This is considered safe, since the populate/discard of sections are already asymmetrical (unplug & unregister all listener section unconditionally). Signed-off-by: Marc-Andr=C3=A9 Lureau --- include/hw/virtio/virtio-mem.h | 3 - include/system/memory.h | 195 ++++++++++++++++------------- include/system/ramblock.h | 3 +- hw/virtio/virtio-mem.c | 163 +++++------------------- system/memory.c | 218 +++++++++++++++++++++++++++++---- system/ram-block-attributes.c | 171 ++++++++------------------ 6 files changed, 385 insertions(+), 368 deletions(-) diff --git a/include/hw/virtio/virtio-mem.h b/include/hw/virtio/virtio-mem.h index 221cfd76bf9..5d1d19c6bec 100644 --- a/include/hw/virtio/virtio-mem.h +++ b/include/hw/virtio/virtio-mem.h @@ -118,9 +118,6 @@ struct VirtIOMEM { /* notifiers to notify when "size" changes */ NotifierList size_change_notifiers; =20 - /* listeners to notify on plug/unplug activity. */ - QLIST_HEAD(, RamDiscardListener) rdl_list; - /* Catch system resets -> qemu_devices_reset() only. */ VirtioMemSystemReset *system_reset; }; diff --git a/include/system/memory.h b/include/system/memory.h index a64b2826489..c6373585a22 100644 --- a/include/system/memory.h +++ b/include/system/memory.h @@ -54,6 +54,12 @@ typedef struct RamDiscardManager RamDiscardManager; DECLARE_OBJ_CHECKERS(RamDiscardManager, RamDiscardManagerClass, RAM_DISCARD_MANAGER, TYPE_RAM_DISCARD_MANAGER); =20 +#define TYPE_RAM_DISCARD_SOURCE "ram-discard-source" +typedef struct RamDiscardSourceClass RamDiscardSourceClass; +typedef struct RamDiscardSource RamDiscardSource; +DECLARE_OBJ_CHECKERS(RamDiscardSource, RamDiscardSourceClass, + RAM_DISCARD_SOURCE, TYPE_RAM_DISCARD_SOURCE); + #ifdef CONFIG_FUZZ void fuzz_dma_read_cb(size_t addr, size_t len, @@ -595,8 +601,8 @@ static inline void ram_discard_listener_init(RamDiscard= Listener *rdl, /** * typedef ReplayRamDiscardState: * - * The callback handler for #RamDiscardManagerClass.replay_populated/ - * #RamDiscardManagerClass.replay_discarded to invoke on populated/discard= ed + * The callback handler for #RamDiscardSourceClass.replay_populated/ + * #RamDiscardSourceClass.replay_discarded to invoke on populated/discarded * parts. * * @section: the #MemoryRegionSection of populated/discarded part @@ -608,40 +614,17 @@ typedef int (*ReplayRamDiscardState)(MemoryRegionSect= ion *section, void *opaque); =20 /* - * RamDiscardManagerClass: - * - * A #RamDiscardManager coordinates which parts of specific RAM #MemoryReg= ion - * regions are currently populated to be used/accessed by the VM, notifying - * after parts were discarded (freeing up memory) and before parts will be - * populated (consuming memory), to be used/accessed by the VM. + * RamDiscardSourceClass: * - * A #RamDiscardManager can only be set for a RAM #MemoryRegion while the - * #MemoryRegion isn't mapped into an address space yet (either directly - * or via an alias); it cannot change while the #MemoryRegion is - * mapped into an address space. - * - * The #RamDiscardManager is intended to be used by technologies that are - * incompatible with discarding of RAM (e.g., VFIO, which may pin all - * memory inside a #MemoryRegion), and require proper coordination to only - * map the currently populated parts, to hinder parts that are expected to - * remain discarded from silently getting populated and consuming memory. - * Technologies that support discarding of RAM don't have to bother and can - * simply map the whole #MemoryRegion. - * - * An example #RamDiscardManager is virtio-mem, which logically (un)plugs - * memory within an assigned RAM #MemoryRegion, coordinated with the VM. - * Logically unplugging memory consists of discarding RAM. The VM agreed t= o not - * access unplugged (discarded) memory - especially via DMA. virtio-mem wi= ll - * properly coordinate with listeners before memory is plugged (populated), - * and after memory is unplugged (discarded). + * A #RamDiscardSource provides information about which parts of a specific + * RAM #MemoryRegion are currently populated (accessible) vs discarded. * - * Listeners are called in multiples of the minimum granularity (unless it - * would exceed the registered range) and changes are aligned to the minim= um - * granularity within the #MemoryRegion. Listeners have to prepare for mem= ory - * becoming discarded in a different granularity than it was populated and= the - * other way around. + * This is an interface that state providers (like virtio-mem or + * RamBlockAttributes) implement to provide discard state information. A + * #RamDiscardManager wraps sources and manages listener registrations and + * notifications. */ -struct RamDiscardManagerClass { +struct RamDiscardSourceClass { /* private */ InterfaceClass parent_class; =20 @@ -651,47 +634,47 @@ struct RamDiscardManagerClass { * @get_min_granularity: * * Get the minimum granularity in which listeners will get notified - * about changes within the #MemoryRegion via the #RamDiscardManager. + * about changes within the #MemoryRegion via the #RamDiscardSource. * - * @rdm: the #RamDiscardManager + * @rds: the #RamDiscardSource * @mr: the #MemoryRegion * * Returns the minimum granularity. */ - uint64_t (*get_min_granularity)(const RamDiscardManager *rdm, + uint64_t (*get_min_granularity)(const RamDiscardSource *rds, const MemoryRegion *mr); =20 /** * @is_populated: * * Check whether the given #MemoryRegionSection is completely populated - * (i.e., no parts are currently discarded) via the #RamDiscardManager. + * (i.e., no parts are currently discarded) via the #RamDiscardSource. * There are no alignment requirements. * - * @rdm: the #RamDiscardManager + * @rds: the #RamDiscardSource * @section: the #MemoryRegionSection * * Returns whether the given range is completely populated. */ - bool (*is_populated)(const RamDiscardManager *rdm, + bool (*is_populated)(const RamDiscardSource *rds, const MemoryRegionSection *section); =20 /** * @replay_populated: * * Call the #ReplayRamDiscardState callback for all populated parts wi= thin - * the #MemoryRegionSection via the #RamDiscardManager. + * the #MemoryRegionSection via the #RamDiscardSource. * * In case any call fails, no further calls are made. * - * @rdm: the #RamDiscardManager + * @rds: the #RamDiscardSource * @section: the #MemoryRegionSection * @replay_fn: the #ReplayRamDiscardState callback * @opaque: pointer to forward to the callback * * Returns 0 on success, or a negative error if any notification faile= d. */ - int (*replay_populated)(const RamDiscardManager *rdm, + int (*replay_populated)(const RamDiscardSource *rds, MemoryRegionSection *section, ReplayRamDiscardState replay_fn, void *opaque); =20 @@ -699,50 +682,60 @@ struct RamDiscardManagerClass { * @replay_discarded: * * Call the #ReplayRamDiscardState callback for all discarded parts wi= thin - * the #MemoryRegionSection via the #RamDiscardManager. + * the #MemoryRegionSection via the #RamDiscardSource. * - * @rdm: the #RamDiscardManager + * @rds: the #RamDiscardSource * @section: the #MemoryRegionSection * @replay_fn: the #ReplayRamDiscardState callback * @opaque: pointer to forward to the callback * * Returns 0 on success, or a negative error if any notification faile= d. */ - int (*replay_discarded)(const RamDiscardManager *rdm, + int (*replay_discarded)(const RamDiscardSource *rds, MemoryRegionSection *section, ReplayRamDiscardState replay_fn, void *opaque); +}; =20 - /** - * @register_listener: - * - * Register a #RamDiscardListener for the given #MemoryRegionSection a= nd - * immediately notify the #RamDiscardListener about all populated parts - * within the #MemoryRegionSection via the #RamDiscardManager. - * - * In case any notification fails, no further notifications are trigge= red - * and an error is logged. - * - * @rdm: the #RamDiscardManager - * @rdl: the #RamDiscardListener - * @section: the #MemoryRegionSection - */ - void (*register_listener)(RamDiscardManager *rdm, - RamDiscardListener *rdl, - MemoryRegionSection *section); +/** + * RamDiscardManager: + * + * A #RamDiscardManager coordinates which parts of specific RAM #MemoryReg= ion + * regions are currently populated to be used/accessed by the VM, notifying + * after parts were discarded (freeing up memory) and before parts will be + * populated (consuming memory), to be used/accessed by the VM. + * + * A #RamDiscardManager can only be set for a RAM #MemoryRegion while the + * #MemoryRegion isn't mapped into an address space yet (either directly + * or via an alias); it cannot change while the #MemoryRegion is + * mapped into an address space. + * + * The #RamDiscardManager is intended to be used by technologies that are + * incompatible with discarding of RAM (e.g., VFIO, which may pin all + * memory inside a #MemoryRegion), and require proper coordination to only + * map the currently populated parts, to hinder parts that are expected to + * remain discarded from silently getting populated and consuming memory. + * Technologies that support discarding of RAM don't have to bother and can + * simply map the whole #MemoryRegion. + * + * An example #RamDiscardSource is virtio-mem, which logically (un)plugs + * memory within an assigned RAM #MemoryRegion, coordinated with the VM. + * Logically unplugging memory consists of discarding RAM. The VM agreed t= o not + * access unplugged (discarded) memory - especially via DMA. virtio-mem wi= ll + * properly coordinate with listeners before memory is plugged (populated), + * and after memory is unplugged (discarded). + * + * Listeners are called in multiples of the minimum granularity (unless it + * would exceed the registered range) and changes are aligned to the minim= um + * granularity within the #MemoryRegion. Listeners have to prepare for mem= ory + * becoming discarded in a different granularity than it was populated and= the + * other way around. + */ +struct RamDiscardManager { + Object parent; =20 - /** - * @unregister_listener: - * - * Unregister a previously registered #RamDiscardListener via the - * #RamDiscardManager after notifying the #RamDiscardListener about all - * populated parts becoming unpopulated within the registered - * #MemoryRegionSection. - * - * @rdm: the #RamDiscardManager - * @rdl: the #RamDiscardListener - */ - void (*unregister_listener)(RamDiscardManager *rdm, - RamDiscardListener *rdl); + RamDiscardSource *rds; + MemoryRegion *mr; + QLIST_HEAD(, RamDiscardListener) rdl_list; }; =20 uint64_t ram_discard_manager_get_min_granularity(const RamDiscardManager *= rdm, @@ -754,8 +747,8 @@ bool ram_discard_manager_is_populated(const RamDiscardM= anager *rdm, /** * ram_discard_manager_replay_populated: * - * A wrapper to call the #RamDiscardManagerClass.replay_populated callback - * of the #RamDiscardManager. + * A wrapper to call the #RamDiscardSourceClass.replay_populated callback + * of the #RamDiscardSource sources. * * @rdm: the #RamDiscardManager * @section: the #MemoryRegionSection @@ -772,8 +765,8 @@ int ram_discard_manager_replay_populated(const RamDisca= rdManager *rdm, /** * ram_discard_manager_replay_discarded: * - * A wrapper to call the #RamDiscardManagerClass.replay_discarded callback - * of the #RamDiscardManager. + * A wrapper to call the #RamDiscardSourceClass.replay_discarded callback + * of the #RamDiscardSource sources. * * @rdm: the #RamDiscardManager * @section: the #MemoryRegionSection @@ -794,6 +787,34 @@ void ram_discard_manager_register_listener(RamDiscardM= anager *rdm, void ram_discard_manager_unregister_listener(RamDiscardManager *rdm, RamDiscardListener *rdl); =20 +/* + * Note: later refactoring should take the source into account and the man= ager + * should be able to aggregate multiple sources. + */ +int ram_discard_manager_notify_populate(RamDiscardManager *rdm, + uint64_t offset, uint64_t size); + + /* + * Note: later refactoring should take the source into account and the ma= nager + * should be able to aggregate multiple sources. + */ +void ram_discard_manager_notify_discard(RamDiscardManager *rdm, + uint64_t offset, uint64_t size); + +/* + * Note: later refactoring should take the source into account and the man= ager + * should be able to aggregate multiple sources. + */ +void ram_discard_manager_notify_discard_all(RamDiscardManager *rdm); + +/* + * Replay populated sections to all registered listeners. + * + * Note: later refactoring should take the source into account and the man= ager + * should be able to aggregate multiple sources. + */ +int ram_discard_manager_replay_populated_to_listeners(RamDiscardManager *r= dm); + /** * memory_translate_iotlb: Extract addresses from a TLB entry. * Called with rcu_read_lock held. @@ -2535,18 +2556,22 @@ static inline bool memory_region_has_ram_discard_ma= nager(MemoryRegion *mr) } =20 /** - * memory_region_set_ram_discard_manager: set the #RamDiscardManager for a + * memory_region_add_ram_discard_source: add a #RamDiscardSource for a * #MemoryRegion * - * This function must not be called for a mapped #MemoryRegion, a #MemoryR= egion - * that does not cover RAM, or a #MemoryRegion that already has a - * #RamDiscardManager assigned. Return 0 if the rdm is set successfully. + * @mr: the #MemoryRegion + * @rdm: #RamDiscardManager to set + */ +int memory_region_add_ram_discard_source(MemoryRegion *mr, RamDiscardSourc= e *source); + +/** + * memory_region_del_ram_discard_source: remove a #RamDiscardSource for a + * #MemoryRegion * * @mr: the #MemoryRegion * @rdm: #RamDiscardManager to set */ -int memory_region_set_ram_discard_manager(MemoryRegion *mr, - RamDiscardManager *rdm); +void memory_region_del_ram_discard_source(MemoryRegion *mr, RamDiscardSour= ce *source); =20 /** * memory_region_find: translate an address/size relative to a diff --git a/include/system/ramblock.h b/include/system/ramblock.h index e9f58ac0457..613beeb1e7d 100644 --- a/include/system/ramblock.h +++ b/include/system/ramblock.h @@ -99,11 +99,10 @@ struct RamBlockAttributes { /* 1-setting of the bitmap represents ram is populated (shared) */ unsigned bitmap_size; unsigned long *bitmap; - - QLIST_HEAD(, RamDiscardListener) rdl_list; }; =20 /* @offset: the offset within the RAMBlock */ + int ram_block_discard_range(RAMBlock *rb, uint64_t offset, size_t length); /* @offset: the offset within the RAMBlock */ int ram_block_discard_guest_memfd_range(RAMBlock *rb, uint64_t offset, diff --git a/hw/virtio/virtio-mem.c b/hw/virtio/virtio-mem.c index a4b71974a1c..be149ee9441 100644 --- a/hw/virtio/virtio-mem.c +++ b/hw/virtio/virtio-mem.c @@ -16,6 +16,7 @@ #include "qemu/error-report.h" #include "qemu/units.h" #include "qemu/target-info-qapi.h" +#include "system/memory.h" #include "system/numa.h" #include "system/system.h" #include "system/ramblock.h" @@ -324,74 +325,31 @@ static int virtio_mem_for_each_unplugged_section(cons= t VirtIOMEM *vmem, return ret; } =20 -static int virtio_mem_notify_populate_cb(MemoryRegionSection *s, void *arg) -{ - RamDiscardListener *rdl =3D arg; - - return rdl->notify_populate(rdl, s); -} - static void virtio_mem_notify_unplug(VirtIOMEM *vmem, uint64_t offset, uint64_t size) { - RamDiscardListener *rdl; + RamDiscardManager *rdm =3D memory_region_get_ram_discard_manager(&vmem= ->memdev->mr); =20 - QLIST_FOREACH(rdl, &vmem->rdl_list, next) { - MemoryRegionSection tmp =3D *rdl->section; - - if (!memory_region_section_intersect_range(&tmp, offset, size)) { - continue; - } - rdl->notify_discard(rdl, &tmp); - } + ram_discard_manager_notify_discard(rdm, offset, size); } =20 static int virtio_mem_notify_plug(VirtIOMEM *vmem, uint64_t offset, uint64_t size) { - RamDiscardListener *rdl, *rdl2; - int ret =3D 0; - - QLIST_FOREACH(rdl, &vmem->rdl_list, next) { - MemoryRegionSection tmp =3D *rdl->section; + RamDiscardManager *rdm =3D memory_region_get_ram_discard_manager(&vmem= ->memdev->mr); =20 - if (!memory_region_section_intersect_range(&tmp, offset, size)) { - continue; - } - ret =3D rdl->notify_populate(rdl, &tmp); - if (ret) { - break; - } - } - - if (ret) { - /* Notify all already-notified listeners. */ - QLIST_FOREACH(rdl2, &vmem->rdl_list, next) { - MemoryRegionSection tmp =3D *rdl2->section; - - if (rdl2 =3D=3D rdl) { - break; - } - if (!memory_region_section_intersect_range(&tmp, offset, size)= ) { - continue; - } - rdl2->notify_discard(rdl2, &tmp); - } - } - return ret; + return ram_discard_manager_notify_populate(rdm, offset, size); } =20 static void virtio_mem_notify_unplug_all(VirtIOMEM *vmem) { - RamDiscardListener *rdl; + RamDiscardManager *rdm =3D memory_region_get_ram_discard_manager(&vmem= ->memdev->mr); =20 if (!vmem->size) { return; } =20 - QLIST_FOREACH(rdl, &vmem->rdl_list, next) { - rdl->notify_discard(rdl, rdl->section); - } + ram_discard_manager_notify_discard_all(rdm); } =20 static bool virtio_mem_is_range_plugged(const VirtIOMEM *vmem, @@ -1037,13 +995,9 @@ static void virtio_mem_device_realize(DeviceState *de= v, Error **errp) return; } =20 - /* - * Set ourselves as RamDiscardManager before the plug handler maps the - * memory region and exposes it via an address space. - */ - if (memory_region_set_ram_discard_manager(&vmem->memdev->mr, - RAM_DISCARD_MANAGER(vmem))) { - error_setg(errp, "Failed to set RamDiscardManager"); + if (memory_region_add_ram_discard_source(&vmem->memdev->mr, + RAM_DISCARD_SOURCE(vmem))) { + error_setg(errp, "Failed to add RAM discard source"); ram_block_coordinated_discard_require(false); return; } @@ -1062,7 +1016,8 @@ static void virtio_mem_device_realize(DeviceState *de= v, Error **errp) ret =3D ram_block_discard_range(rb, 0, qemu_ram_get_used_length(rb= )); if (ret) { error_setg_errno(errp, -ret, "Unexpected error discarding RAM"= ); - memory_region_set_ram_discard_manager(&vmem->memdev->mr, NULL); + memory_region_del_ram_discard_source(&vmem->memdev->mr, + RAM_DISCARD_SOURCE(vmem)); ram_block_coordinated_discard_require(false); return; } @@ -1147,7 +1102,7 @@ static void virtio_mem_device_unrealize(DeviceState *= dev) * The unplug handler unmapped the memory region, it cannot be * found via an address space anymore. Unset ourselves. */ - memory_region_set_ram_discard_manager(&vmem->memdev->mr, NULL); + memory_region_del_ram_discard_source(&vmem->memdev->mr, RAM_DISCARD_SO= URCE(vmem)); ram_block_coordinated_discard_require(false); } =20 @@ -1175,9 +1130,7 @@ static int virtio_mem_activate_memslot_range_cb(VirtI= OMEM *vmem, void *arg, =20 static int virtio_mem_post_load_bitmap(VirtIOMEM *vmem) { - RamDiscardListener *rdl; - int ret; - + RamDiscardManager *rdm =3D memory_region_get_ram_discard_manager(&vmem= ->memdev->mr); /* * We restored the bitmap and updated the requested size; activate all * memslots (so listeners register) before notifying about plugged blo= cks. @@ -1195,14 +1148,7 @@ static int virtio_mem_post_load_bitmap(VirtIOMEM *vm= em) * We started out with all memory discarded and our memory region is m= apped * into an address space. Replay, now that we updated the bitmap. */ - QLIST_FOREACH(rdl, &vmem->rdl_list, next) { - ret =3D virtio_mem_for_each_plugged_section(vmem, rdl->section, rd= l, - virtio_mem_notify_populat= e_cb); - if (ret) { - return ret; - } - } - return 0; + return ram_discard_manager_replay_populated_to_listeners(rdm); } =20 static int virtio_mem_post_load(void *opaque, int version_id) @@ -1650,7 +1596,6 @@ static void virtio_mem_instance_init(Object *obj) VirtIOMEM *vmem =3D VIRTIO_MEM(obj); =20 notifier_list_init(&vmem->size_change_notifiers); - QLIST_INIT(&vmem->rdl_list); =20 object_property_add(obj, VIRTIO_MEM_SIZE_PROP, "size", virtio_mem_get_= size, NULL, NULL, NULL); @@ -1694,19 +1639,19 @@ static const Property virtio_mem_legacy_guests_prop= erties[] =3D { unplugged_inaccessible, ON_OFF_AUTO_ON), }; =20 -static uint64_t virtio_mem_rdm_get_min_granularity(const RamDiscardManager= *rdm, +static uint64_t virtio_mem_rds_get_min_granularity(const RamDiscardSource = *rds, const MemoryRegion *mr) { - const VirtIOMEM *vmem =3D VIRTIO_MEM(rdm); + const VirtIOMEM *vmem =3D VIRTIO_MEM(rds); =20 g_assert(mr =3D=3D &vmem->memdev->mr); return vmem->block_size; } =20 -static bool virtio_mem_rdm_is_populated(const RamDiscardManager *rdm, +static bool virtio_mem_rds_is_populated(const RamDiscardSource *rds, const MemoryRegionSection *s) { - const VirtIOMEM *vmem =3D VIRTIO_MEM(rdm); + const VirtIOMEM *vmem =3D VIRTIO_MEM(rds); uint64_t start_gpa =3D vmem->addr + s->offset_within_region; uint64_t end_gpa =3D start_gpa + int128_get64(s->size); =20 @@ -1727,19 +1672,19 @@ struct VirtIOMEMReplayData { void *opaque; }; =20 -static int virtio_mem_rdm_replay_populated_cb(MemoryRegionSection *s, void= *arg) +static int virtio_mem_rds_replay_cb(MemoryRegionSection *s, void *arg) { struct VirtIOMEMReplayData *data =3D arg; =20 return data->fn(s, data->opaque); } =20 -static int virtio_mem_rdm_replay_populated(const RamDiscardManager *rdm, +static int virtio_mem_rds_replay_populated(const RamDiscardSource *rds, MemoryRegionSection *s, ReplayRamDiscardState replay_fn, void *opaque) { - const VirtIOMEM *vmem =3D VIRTIO_MEM(rdm); + const VirtIOMEM *vmem =3D VIRTIO_MEM(rds); struct VirtIOMEMReplayData data =3D { .fn =3D replay_fn, .opaque =3D opaque, @@ -1747,23 +1692,15 @@ static int virtio_mem_rdm_replay_populated(const Ra= mDiscardManager *rdm, =20 g_assert(s->mr =3D=3D &vmem->memdev->mr); return virtio_mem_for_each_plugged_section(vmem, s, &data, - virtio_mem_rdm_replay_populate= d_cb); -} - -static int virtio_mem_rdm_replay_discarded_cb(MemoryRegionSection *s, - void *arg) -{ - struct VirtIOMEMReplayData *data =3D arg; - - return data->fn(s, data->opaque); + virtio_mem_rds_replay_cb); } =20 -static int virtio_mem_rdm_replay_discarded(const RamDiscardManager *rdm, +static int virtio_mem_rds_replay_discarded(const RamDiscardSource *rds, MemoryRegionSection *s, ReplayRamDiscardState replay_fn, void *opaque) { - const VirtIOMEM *vmem =3D VIRTIO_MEM(rdm); + const VirtIOMEM *vmem =3D VIRTIO_MEM(rds); struct VirtIOMEMReplayData data =3D { .fn =3D replay_fn, .opaque =3D opaque, @@ -1771,41 +1708,7 @@ static int virtio_mem_rdm_replay_discarded(const Ram= DiscardManager *rdm, =20 g_assert(s->mr =3D=3D &vmem->memdev->mr); return virtio_mem_for_each_unplugged_section(vmem, s, &data, - virtio_mem_rdm_replay_dis= carded_cb); -} - -static void virtio_mem_rdm_register_listener(RamDiscardManager *rdm, - RamDiscardListener *rdl, - MemoryRegionSection *s) -{ - VirtIOMEM *vmem =3D VIRTIO_MEM(rdm); - int ret; - - g_assert(s->mr =3D=3D &vmem->memdev->mr); - rdl->section =3D memory_region_section_new_copy(s); - - QLIST_INSERT_HEAD(&vmem->rdl_list, rdl, next); - ret =3D virtio_mem_for_each_plugged_section(vmem, rdl->section, rdl, - virtio_mem_notify_populate_c= b); - if (ret) { - error_report("%s: Replaying plugged ranges failed: %s", __func__, - strerror(-ret)); - } -} - -static void virtio_mem_rdm_unregister_listener(RamDiscardManager *rdm, - RamDiscardListener *rdl) -{ - VirtIOMEM *vmem =3D VIRTIO_MEM(rdm); - - g_assert(rdl->section->mr =3D=3D &vmem->memdev->mr); - if (vmem->size) { - rdl->notify_discard(rdl, rdl->section); - } - - memory_region_section_free_copy(rdl->section); - rdl->section =3D NULL; - QLIST_REMOVE(rdl, next); + virtio_mem_rds_replay_cb); } =20 static void virtio_mem_unplug_request_check(VirtIOMEM *vmem, Error **errp) @@ -1837,7 +1740,7 @@ static void virtio_mem_class_init(ObjectClass *klass,= const void *data) DeviceClass *dc =3D DEVICE_CLASS(klass); VirtioDeviceClass *vdc =3D VIRTIO_DEVICE_CLASS(klass); VirtIOMEMClass *vmc =3D VIRTIO_MEM_CLASS(klass); - RamDiscardManagerClass *rdmc =3D RAM_DISCARD_MANAGER_CLASS(klass); + RamDiscardSourceClass *rdsc =3D RAM_DISCARD_SOURCE_CLASS(klass); =20 device_class_set_props(dc, virtio_mem_properties); if (virtio_mem_has_legacy_guests()) { @@ -1861,12 +1764,10 @@ static void virtio_mem_class_init(ObjectClass *klas= s, const void *data) vmc->remove_size_change_notifier =3D virtio_mem_remove_size_change_not= ifier; vmc->unplug_request_check =3D virtio_mem_unplug_request_check; =20 - rdmc->get_min_granularity =3D virtio_mem_rdm_get_min_granularity; - rdmc->is_populated =3D virtio_mem_rdm_is_populated; - rdmc->replay_populated =3D virtio_mem_rdm_replay_populated; - rdmc->replay_discarded =3D virtio_mem_rdm_replay_discarded; - rdmc->register_listener =3D virtio_mem_rdm_register_listener; - rdmc->unregister_listener =3D virtio_mem_rdm_unregister_listener; + rdsc->get_min_granularity =3D virtio_mem_rds_get_min_granularity; + rdsc->is_populated =3D virtio_mem_rds_is_populated; + rdsc->replay_populated =3D virtio_mem_rds_replay_populated; + rdsc->replay_discarded =3D virtio_mem_rds_replay_discarded; } =20 static const TypeInfo virtio_mem_info =3D { @@ -1878,7 +1779,7 @@ static const TypeInfo virtio_mem_info =3D { .class_init =3D virtio_mem_class_init, .class_size =3D sizeof(VirtIOMEMClass), .interfaces =3D (const InterfaceInfo[]) { - { TYPE_RAM_DISCARD_MANAGER }, + { TYPE_RAM_DISCARD_SOURCE }, { } }, }; diff --git a/system/memory.c b/system/memory.c index c51d0798a84..3e7fd759692 100644 --- a/system/memory.c +++ b/system/memory.c @@ -2105,34 +2105,88 @@ RamDiscardManager *memory_region_get_ram_discard_ma= nager(MemoryRegion *mr) return mr->rdm; } =20 -int memory_region_set_ram_discard_manager(MemoryRegion *mr, - RamDiscardManager *rdm) +static RamDiscardManager *ram_discard_manager_new(MemoryRegion *mr, + RamDiscardSource *rds) +{ + RamDiscardManager *rdm =3D RAM_DISCARD_MANAGER(object_new(TYPE_RAM_DIS= CARD_MANAGER)); + + rdm->rds =3D rds; + rdm->mr =3D mr; + QLIST_INIT(&rdm->rdl_list); + return rdm; +} + +int memory_region_add_ram_discard_source(MemoryRegion *mr, + RamDiscardSource *source) { g_assert(memory_region_is_ram(mr)); - if (mr->rdm && rdm) { + if (mr->rdm) { return -EBUSY; } =20 - mr->rdm =3D rdm; + mr->rdm =3D ram_discard_manager_new(mr, RAM_DISCARD_SOURCE(source)); return 0; } =20 +void memory_region_del_ram_discard_source(MemoryRegion *mr, + RamDiscardSource *source) +{ + g_assert(mr->rdm->rds =3D=3D source); + + object_unref(mr->rdm); + mr->rdm =3D NULL; +} + +static uint64_t ram_discard_source_get_min_granularity(const RamDiscardSou= rce *rds, + const MemoryRegion = *mr) +{ + RamDiscardSourceClass *rdsc =3D RAM_DISCARD_SOURCE_GET_CLASS(rds); + + g_assert(rdsc->get_min_granularity); + return rdsc->get_min_granularity(rds, mr); +} + +static bool ram_discard_source_is_populated(const RamDiscardSource *rds, + const MemoryRegionSection *sec= tion) +{ + RamDiscardSourceClass *rdsc =3D RAM_DISCARD_SOURCE_GET_CLASS(rds); + + g_assert(rdsc->is_populated); + return rdsc->is_populated(rds, section); +} + +static int ram_discard_source_replay_populated(const RamDiscardSource *rds, + MemoryRegionSection *sectio= n, + ReplayRamDiscardState repla= y_fn, + void *opaque) +{ + RamDiscardSourceClass *rdsc =3D RAM_DISCARD_SOURCE_GET_CLASS(rds); + + g_assert(rdsc->replay_populated); + return rdsc->replay_populated(rds, section, replay_fn, opaque); +} + +static int ram_discard_source_replay_discarded(const RamDiscardSource *rds, + MemoryRegionSection *sectio= n, + ReplayRamDiscardState repla= y_fn, + void *opaque) +{ + RamDiscardSourceClass *rdsc =3D RAM_DISCARD_SOURCE_GET_CLASS(rds); + + g_assert(rdsc->replay_discarded); + return rdsc->replay_discarded(rds, section, replay_fn, opaque); +} + uint64_t ram_discard_manager_get_min_granularity(const RamDiscardManager *= rdm, const MemoryRegion *mr) { - RamDiscardManagerClass *rdmc =3D RAM_DISCARD_MANAGER_GET_CLASS(rdm); - - g_assert(rdmc->get_min_granularity); - return rdmc->get_min_granularity(rdm, mr); + return ram_discard_source_get_min_granularity(rdm->rds, mr); } =20 bool ram_discard_manager_is_populated(const RamDiscardManager *rdm, const MemoryRegionSection *section) { - RamDiscardManagerClass *rdmc =3D RAM_DISCARD_MANAGER_GET_CLASS(rdm); - - g_assert(rdmc->is_populated); - return rdmc->is_populated(rdm, section); + return ram_discard_source_is_populated(rdm->rds, section); } =20 int ram_discard_manager_replay_populated(const RamDiscardManager *rdm, @@ -2140,10 +2194,7 @@ int ram_discard_manager_replay_populated(const RamDi= scardManager *rdm, ReplayRamDiscardState replay_fn, void *opaque) { - RamDiscardManagerClass *rdmc =3D RAM_DISCARD_MANAGER_GET_CLASS(rdm); - - g_assert(rdmc->replay_populated); - return rdmc->replay_populated(rdm, section, replay_fn, opaque); + return ram_discard_source_replay_populated(rdm->rds, section, replay_f= n, opaque); } =20 int ram_discard_manager_replay_discarded(const RamDiscardManager *rdm, @@ -2151,29 +2202,133 @@ int ram_discard_manager_replay_discarded(const Ram= DiscardManager *rdm, ReplayRamDiscardState replay_fn, void *opaque) { - RamDiscardManagerClass *rdmc =3D RAM_DISCARD_MANAGER_GET_CLASS(rdm); + return ram_discard_source_replay_discarded(rdm->rds, section, replay_f= n, opaque); +} + +static void ram_discard_manager_initfn(Object *obj) +{ + RamDiscardManager *rdm =3D RAM_DISCARD_MANAGER(obj); + + QLIST_INIT(&rdm->rdl_list); +} + +static void ram_discard_manager_finalize(Object *obj) +{ + RamDiscardManager *rdm =3D RAM_DISCARD_MANAGER(obj); =20 - g_assert(rdmc->replay_discarded); - return rdmc->replay_discarded(rdm, section, replay_fn, opaque); + g_assert(QLIST_EMPTY(&rdm->rdl_list)); +} + +int ram_discard_manager_notify_populate(RamDiscardManager *rdm, + uint64_t offset, uint64_t size) +{ + RamDiscardListener *rdl, *rdl2; + int ret =3D 0; + + QLIST_FOREACH(rdl, &rdm->rdl_list, next) { + MemoryRegionSection tmp =3D *rdl->section; + + if (!memory_region_section_intersect_range(&tmp, offset, size)) { + continue; + } + ret =3D rdl->notify_populate(rdl, &tmp); + if (ret) { + break; + } + } + + if (ret) { + /* Notify all already-notified listeners about discard. */ + QLIST_FOREACH(rdl2, &rdm->rdl_list, next) { + MemoryRegionSection tmp =3D *rdl2->section; + + if (rdl2 =3D=3D rdl) { + break; + } + if (!memory_region_section_intersect_range(&tmp, offset, size)= ) { + continue; + } + rdl2->notify_discard(rdl2, &tmp); + } + } + return ret; +} + +void ram_discard_manager_notify_discard(RamDiscardManager *rdm, + uint64_t offset, uint64_t size) +{ + RamDiscardListener *rdl; + + QLIST_FOREACH(rdl, &rdm->rdl_list, next) { + MemoryRegionSection tmp =3D *rdl->section; + + if (!memory_region_section_intersect_range(&tmp, offset, size)) { + continue; + } + rdl->notify_discard(rdl, &tmp); + } +} + +void ram_discard_manager_notify_discard_all(RamDiscardManager *rdm) +{ + RamDiscardListener *rdl; + + QLIST_FOREACH(rdl, &rdm->rdl_list, next) { + rdl->notify_discard(rdl, rdl->section); + } +} + +static int rdm_populate_cb(MemoryRegionSection *section, void *opaque) +{ + RamDiscardListener *rdl =3D opaque; + + return rdl->notify_populate(rdl, section); } =20 void ram_discard_manager_register_listener(RamDiscardManager *rdm, RamDiscardListener *rdl, MemoryRegionSection *section) { - RamDiscardManagerClass *rdmc =3D RAM_DISCARD_MANAGER_GET_CLASS(rdm); + int ret; + + g_assert(section->mr =3D=3D rdm->mr); + + rdl->section =3D memory_region_section_new_copy(section); + QLIST_INSERT_HEAD(&rdm->rdl_list, rdl, next); =20 - g_assert(rdmc->register_listener); - rdmc->register_listener(rdm, rdl, section); + ret =3D ram_discard_source_replay_populated(rdm->rds, rdl->section, + rdm_populate_cb, rdl); + if (ret) { + error_report("%s: Replaying populated ranges failed: %s", __func__, + strerror(-ret)); + } } =20 void ram_discard_manager_unregister_listener(RamDiscardManager *rdm, RamDiscardListener *rdl) { - RamDiscardManagerClass *rdmc =3D RAM_DISCARD_MANAGER_GET_CLASS(rdm); + g_assert(rdl->section); + g_assert(rdl->section->mr =3D=3D rdm->mr); + + rdl->notify_discard(rdl, rdl->section); + memory_region_section_free_copy(rdl->section); + rdl->section =3D NULL; + QLIST_REMOVE(rdl, next); +} + +int ram_discard_manager_replay_populated_to_listeners(RamDiscardManager *r= dm) +{ + RamDiscardListener *rdl; + int ret =3D 0; =20 - g_assert(rdmc->unregister_listener); - rdmc->unregister_listener(rdm, rdl); + QLIST_FOREACH(rdl, &rdm->rdl_list, next) { + ret =3D ram_discard_source_replay_populated(rdm->rds, rdl->section, + rdm_populate_cb, rdl); + if (ret) { + break; + } + } + return ret; } =20 /* Called with rcu_read_lock held. */ @@ -3838,9 +3993,17 @@ static const TypeInfo iommu_memory_region_info =3D { }; =20 static const TypeInfo ram_discard_manager_info =3D { - .parent =3D TYPE_INTERFACE, + .parent =3D TYPE_OBJECT, .name =3D TYPE_RAM_DISCARD_MANAGER, - .class_size =3D sizeof(RamDiscardManagerClass), + .instance_size =3D sizeof(RamDiscardManager), + .instance_init =3D ram_discard_manager_initfn, + .instance_finalize =3D ram_discard_manager_finalize, +}; + +static const TypeInfo ram_discard_source_info =3D { + .parent =3D TYPE_INTERFACE, + .name =3D TYPE_RAM_DISCARD_SOURCE, + .class_size =3D sizeof(RamDiscardSourceClass), }; =20 static void memory_register_types(void) @@ -3848,6 +4011,7 @@ static void memory_register_types(void) type_register_static(&memory_region_info); type_register_static(&iommu_memory_region_info); type_register_static(&ram_discard_manager_info); + type_register_static(&ram_discard_source_info); } =20 type_init(memory_register_types) diff --git a/system/ram-block-attributes.c b/system/ram-block-attributes.c index 630b0fda126..ceb7066e6b9 100644 --- a/system/ram-block-attributes.c +++ b/system/ram-block-attributes.c @@ -18,7 +18,7 @@ OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(RamBlockAttribu= tes, ram_block_attributes, RAM_BLOCK_ATTRIBUTES, OBJECT, - { TYPE_RAM_DISCARD_MANAGER }, + { TYPE_RAM_DISCARD_SOURCE }, { }) =20 static size_t @@ -32,35 +32,9 @@ ram_block_attributes_get_block_size(void) return qemu_real_host_page_size(); } =20 - -static bool -ram_block_attributes_rdm_is_populated(const RamDiscardManager *rdm, - const MemoryRegionSection *section) -{ - const RamBlockAttributes *attr =3D RAM_BLOCK_ATTRIBUTES(rdm); - const size_t block_size =3D ram_block_attributes_get_block_size(); - const uint64_t first_bit =3D section->offset_within_region / block_siz= e; - const uint64_t last_bit =3D - first_bit + int128_get64(section->size) / block_size - 1; - unsigned long first_discarded_bit; - - first_discarded_bit =3D find_next_zero_bit(attr->bitmap, last_bit + 1, - first_bit); - return first_discarded_bit > last_bit; -} - typedef int (*ram_block_attributes_section_cb)(MemoryRegionSection *s, void *arg); =20 -static int -ram_block_attributes_notify_populate_cb(MemoryRegionSection *section, - void *arg) -{ - RamDiscardListener *rdl =3D arg; - - return rdl->notify_populate(rdl, section); -} - static int ram_block_attributes_for_each_populated_section(const RamBlockAttributes *= attr, MemoryRegionSection *secti= on, @@ -144,93 +118,73 @@ ram_block_attributes_for_each_discarded_section(const= RamBlockAttributes *attr, return ret; } =20 -static uint64_t -ram_block_attributes_rdm_get_min_granularity(const RamDiscardManager *rdm, - const MemoryRegion *mr) -{ - const RamBlockAttributes *attr =3D RAM_BLOCK_ATTRIBUTES(rdm); =20 - g_assert(mr =3D=3D attr->ram_block->mr); - return ram_block_attributes_get_block_size(); -} +typedef struct RamBlockAttributesReplayData { + ReplayRamDiscardState fn; + void *opaque; +} RamBlockAttributesReplayData; =20 -static void -ram_block_attributes_rdm_register_listener(RamDiscardManager *rdm, - RamDiscardListener *rdl, - MemoryRegionSection *section) +static int ram_block_attributes_rds_replay_cb(MemoryRegionSection *section, + void *arg) { - RamBlockAttributes *attr =3D RAM_BLOCK_ATTRIBUTES(rdm); - int ret; - - g_assert(section->mr =3D=3D attr->ram_block->mr); - rdl->section =3D memory_region_section_new_copy(section); - - QLIST_INSERT_HEAD(&attr->rdl_list, rdl, next); + RamBlockAttributesReplayData *data =3D arg; =20 - ret =3D ram_block_attributes_for_each_populated_section(attr, section,= rdl, - ram_block_attributes_notify_populate_c= b); - if (ret) { - error_report("%s: Failed to register RAM discard listener: %s", - __func__, strerror(-ret)); - exit(1); - } + return data->fn(section, data->opaque); } =20 -static void -ram_block_attributes_rdm_unregister_listener(RamDiscardManager *rdm, - RamDiscardListener *rdl) +/* RamDiscardSource interface implementation */ +static uint64_t +ram_block_attributes_rds_get_min_granularity(const RamDiscardSource *rds, + const MemoryRegion *mr) { - RamBlockAttributes *attr =3D RAM_BLOCK_ATTRIBUTES(rdm); + const RamBlockAttributes *attr =3D RAM_BLOCK_ATTRIBUTES(rds); =20 - g_assert(rdl->section); - g_assert(rdl->section->mr =3D=3D attr->ram_block->mr); - - rdl->notify_discard(rdl, rdl->section); - - memory_region_section_free_copy(rdl->section); - rdl->section =3D NULL; - QLIST_REMOVE(rdl, next); + g_assert(mr =3D=3D attr->ram_block->mr); + return ram_block_attributes_get_block_size(); } =20 -typedef struct RamBlockAttributesReplayData { - ReplayRamDiscardState fn; - void *opaque; -} RamBlockAttributesReplayData; - -static int ram_block_attributes_rdm_replay_cb(MemoryRegionSection *section, - void *arg) +static bool +ram_block_attributes_rds_is_populated(const RamDiscardSource *rds, + const MemoryRegionSection *section) { - RamBlockAttributesReplayData *data =3D arg; + const RamBlockAttributes *attr =3D RAM_BLOCK_ATTRIBUTES(rds); + const size_t block_size =3D ram_block_attributes_get_block_size(); + const uint64_t first_bit =3D section->offset_within_region / block_siz= e; + const uint64_t last_bit =3D + first_bit + int128_get64(section->size) / block_size - 1; + unsigned long first_discarded_bit; =20 - return data->fn(section, data->opaque); + first_discarded_bit =3D find_next_zero_bit(attr->bitmap, last_bit + 1, + first_bit); + return first_discarded_bit > last_bit; } =20 static int -ram_block_attributes_rdm_replay_populated(const RamDiscardManager *rdm, +ram_block_attributes_rds_replay_populated(const RamDiscardSource *rds, MemoryRegionSection *section, ReplayRamDiscardState replay_fn, void *opaque) { - RamBlockAttributes *attr =3D RAM_BLOCK_ATTRIBUTES(rdm); + RamBlockAttributes *attr =3D RAM_BLOCK_ATTRIBUTES(rds); RamBlockAttributesReplayData data =3D { .fn =3D replay_fn, .opaque =3D= opaque }; =20 g_assert(section->mr =3D=3D attr->ram_block->mr); return ram_block_attributes_for_each_populated_section(attr, section, = &data, - ram_block_attributes_rdm_repla= y_cb); + ram_block_attri= butes_rds_replay_cb); } =20 static int -ram_block_attributes_rdm_replay_discarded(const RamDiscardManager *rdm, +ram_block_attributes_rds_replay_discarded(const RamDiscardSource *rds, MemoryRegionSection *section, ReplayRamDiscardState replay_fn, void *opaque) { - RamBlockAttributes *attr =3D RAM_BLOCK_ATTRIBUTES(rdm); + RamBlockAttributes *attr =3D RAM_BLOCK_ATTRIBUTES(rds); RamBlockAttributesReplayData data =3D { .fn =3D replay_fn, .opaque =3D= opaque }; =20 g_assert(section->mr =3D=3D attr->ram_block->mr); return ram_block_attributes_for_each_discarded_section(attr, section, = &data, - ram_block_attributes_rdm_repla= y_cb); + ram_block_attri= butes_rds_replay_cb); } =20 static bool @@ -257,42 +211,23 @@ ram_block_attributes_is_valid_range(RamBlockAttribute= s *attr, uint64_t offset, return true; } =20 -static void ram_block_attributes_notify_discard(RamBlockAttributes *attr, - uint64_t offset, - uint64_t size) +static void +ram_block_attributes_notify_discard(RamBlockAttributes *attr, + uint64_t offset, + uint64_t size) { - RamDiscardListener *rdl; + RamDiscardManager *rdm =3D memory_region_get_ram_discard_manager(attr-= >ram_block->mr); =20 - QLIST_FOREACH(rdl, &attr->rdl_list, next) { - MemoryRegionSection tmp =3D *rdl->section; - - if (!memory_region_section_intersect_range(&tmp, offset, size)) { - continue; - } - rdl->notify_discard(rdl, &tmp); - } + ram_discard_manager_notify_discard(rdm, offset, size); } =20 static int ram_block_attributes_notify_populate(RamBlockAttributes *attr, uint64_t offset, uint64_t size) { - RamDiscardListener *rdl; - int ret =3D 0; - - QLIST_FOREACH(rdl, &attr->rdl_list, next) { - MemoryRegionSection tmp =3D *rdl->section; - - if (!memory_region_section_intersect_range(&tmp, offset, size)) { - continue; - } - ret =3D rdl->notify_populate(rdl, &tmp); - if (ret) { - break; - } - } + RamDiscardManager *rdm =3D memory_region_get_ram_discard_manager(attr-= >ram_block->mr); =20 - return ret; + return ram_discard_manager_notify_populate(rdm, offset, size); } =20 int ram_block_attributes_state_change(RamBlockAttributes *attr, @@ -376,7 +311,8 @@ RamBlockAttributes *ram_block_attributes_create(RAMBloc= k *ram_block) attr =3D RAM_BLOCK_ATTRIBUTES(object_new(TYPE_RAM_BLOCK_ATTRIBUTES)); =20 attr->ram_block =3D ram_block; - if (memory_region_set_ram_discard_manager(mr, RAM_DISCARD_MANAGER(attr= ))) { + + if (memory_region_add_ram_discard_source(mr, RAM_DISCARD_SOURCE(attr))= ) { object_unref(OBJECT(attr)); return NULL; } @@ -391,15 +327,12 @@ void ram_block_attributes_destroy(RamBlockAttributes = *attr) g_assert(attr); =20 g_free(attr->bitmap); - memory_region_set_ram_discard_manager(attr->ram_block->mr, NULL); + memory_region_del_ram_discard_source(attr->ram_block->mr, RAM_DISCARD_= SOURCE(attr)); object_unref(OBJECT(attr)); } =20 static void ram_block_attributes_init(Object *obj) { - RamBlockAttributes *attr =3D RAM_BLOCK_ATTRIBUTES(obj); - - QLIST_INIT(&attr->rdl_list); } =20 static void ram_block_attributes_finalize(Object *obj) @@ -409,12 +342,10 @@ static void ram_block_attributes_finalize(Object *obj) static void ram_block_attributes_class_init(ObjectClass *klass, const void *data) { - RamDiscardManagerClass *rdmc =3D RAM_DISCARD_MANAGER_CLASS(klass); - - rdmc->get_min_granularity =3D ram_block_attributes_rdm_get_min_granula= rity; - rdmc->register_listener =3D ram_block_attributes_rdm_register_listener; - rdmc->unregister_listener =3D ram_block_attributes_rdm_unregister_list= ener; - rdmc->is_populated =3D ram_block_attributes_rdm_is_populated; - rdmc->replay_populated =3D ram_block_attributes_rdm_replay_populated; - rdmc->replay_discarded =3D ram_block_attributes_rdm_replay_discarded; + RamDiscardSourceClass *rdsc =3D RAM_DISCARD_SOURCE_CLASS(klass); + + rdsc->get_min_granularity =3D ram_block_attributes_rds_get_min_granula= rity; + rdsc->is_populated =3D ram_block_attributes_rds_is_populated; + rdsc->replay_populated =3D ram_block_attributes_rds_replay_populated; + rdsc->replay_discarded =3D ram_block_attributes_rds_replay_discarded; } --=20 2.52.0