From nobody Fri Apr 10 01:06:04 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id EAE341C2324; Thu, 5 Mar 2026 05:31:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772688720; cv=none; b=eswmuh08wuQz763DAohhpIC7X5pZNrhAM6S/LGS6IMRoz0WYm1ddxdbBxcskelazfMhocDyDjVEIYHToetJdsJ+m3FzMYXWfft9AA82wOx57MTqV5r7P7XwOm7J6XIBK4O1cf/w0ZtHOkzOtuBW1foh58nWcN7Z6rFlfvyhjAME= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772688720; c=relaxed/simple; bh=I5GagJ4JJrwaP1VFcc3/YKczD7L+HER36RxUyplKAQk=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=FIr2D2ID4Jimfbam+zooAPeD6/tPhfT2H7zd0rFzAo5KVzcrc+mQOiNMEn6XfKZ0EQsi2P6ecTUoVJ91IqizJFqEZAnE8ALgKajGQdgn/N9yHlYG7iRn6HNAf34T3RuLBbeoUfzhtn/SaBrOOFfiDWVzLWhKVVuVlaH8bDf2mIw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 442C2497; Wed, 4 Mar 2026 21:31:52 -0800 (PST) Received: from ergosum.cambridge.arm.com (ergosum.cambridge.arm.com [10.1.196.45]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPA id C44F33F694; Wed, 4 Mar 2026 21:31:56 -0800 (PST) From: Anshuman Khandual To: linux-arm-kernel@lists.infradead.org Cc: Anshuman Khandual , Catalin Marinas , Will Deacon , Ryan Roberts , David Hildenbrand , Yang Shi , Christoph Lameter , linux-kernel@vger.kernel.org, stable@vger.kernel.org Subject: [PATCH V4 1/2] arm64/mm: Enable batched TLB flush in unmap_hotplug_range() Date: Thu, 5 Mar 2026 05:31:46 +0000 Message-Id: <20260305053148.3765710-2-anshuman.khandual@arm.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20260305053148.3765710-1-anshuman.khandual@arm.com> References: <20260305053148.3765710-1-anshuman.khandual@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" During a memory hot remove operation, both linear and vmemmap mappings for the memory range being removed, get unmapped via unmap_hotplug_range() but mapped pages get freed only for vmemmap mapping. This is just a sequential operation where each table entry gets cleared, followed by a leaf specific TLB flush, and then followed by memory free operation when applicable. This approach was simple and uniform both for vmemmap and linear mappings. But linear mapping might contain CONT marked block memory where it becomes necessary to first clear out all entire in the range before a TLB flush. This is as per the architecture requirement. Hence batch all TLB flushes during the table tear down walk and finally do it in unmap_hotplug_range(). Prior to this fix, it was hypothetically possible for a speculative access to a higher address in the contiguous block to fill the TLB with shattered entries for the entire contiguous range after a lower address had already been cleared and invalidated. Due to the table entries being shattered, the subsequent TLB invalidation for the higher address would not then clear the TLB entries for the lower address, meaning stale TLB entries could persist. Besides it also helps in improving the performance via TLBI range operation along with reduced synchronization instructions. The time spent executing unmap_hotplug_range() improved 97% measured over a 2GB memory hot removal in KVM guest. This scheme is not applicable during vmemmap mapping tear down where memory needs to be freed and hence a TLB flush is required after clearing out page table entry. Cc: Catalin Marinas Cc: Will Deacon Cc: linux-arm-kernel@lists.infradead.org Cc: linux-kernel@vger.kernel.org Closes: https://lore.kernel.org/all/aWZYXhrT6D2M-7-N@willie-the-truck/ Fixes: bbd6ec605c0f ("arm64/mm: Enable memory hot remove") Cc: stable@vger.kernel.org Reviewed-by: Ryan Roberts Signed-off-by: Ryan Roberts Signed-off-by: Anshuman Khandual Reviewed-by: David Hildenbrand (Arm) --- arch/arm64/mm/mmu.c | 46 +++++++++++++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c index a6a00accf4f9..f7ccda22d39e 100644 --- a/arch/arm64/mm/mmu.c +++ b/arch/arm64/mm/mmu.c @@ -1458,10 +1458,18 @@ static void unmap_hotplug_pte_range(pmd_t *pmdp, un= signed long addr, =20 WARN_ON(!pte_present(pte)); __pte_clear(&init_mm, addr, ptep); - flush_tlb_kernel_range(addr, addr + PAGE_SIZE); - if (free_mapped) + if (free_mapped) { + /* + * CONT blocks are not supported in the vmemmap + */ + WARN_ON(pte_cont(pte)); + flush_tlb_kernel_range(addr, addr + PAGE_SIZE); free_hotplug_page_range(pte_page(pte), PAGE_SIZE, altmap); + } + /* + * unmap_hotplug_range() flushes TLB for !free_mapped + */ } while (addr +=3D PAGE_SIZE, addr < end); } =20 @@ -1482,15 +1490,18 @@ static void unmap_hotplug_pmd_range(pud_t *pudp, un= signed long addr, WARN_ON(!pmd_present(pmd)); if (pmd_sect(pmd)) { pmd_clear(pmdp); - - /* - * One TLBI should be sufficient here as the PMD_SIZE - * range is mapped with a single block entry. - */ - flush_tlb_kernel_range(addr, addr + PAGE_SIZE); - if (free_mapped) + if (free_mapped) { + /* + * CONT blocks are not supported in the vmemmap + */ + WARN_ON(pmd_cont(pmd)); + flush_tlb_kernel_range(addr, addr + PMD_SIZE); free_hotplug_page_range(pmd_page(pmd), PMD_SIZE, altmap); + } + /* + * unmap_hotplug_range() flushes TLB for !free_mapped + */ continue; } WARN_ON(!pmd_table(pmd)); @@ -1515,15 +1526,14 @@ static void unmap_hotplug_pud_range(p4d_t *p4dp, un= signed long addr, WARN_ON(!pud_present(pud)); if (pud_sect(pud)) { pud_clear(pudp); - - /* - * One TLBI should be sufficient here as the PUD_SIZE - * range is mapped with a single block entry. - */ - flush_tlb_kernel_range(addr, addr + PAGE_SIZE); - if (free_mapped) + if (free_mapped) { + flush_tlb_kernel_range(addr, addr + PUD_SIZE); free_hotplug_page_range(pud_page(pud), PUD_SIZE, altmap); + } + /* + * unmap_hotplug_range() flushes TLB for !free_mapped + */ continue; } WARN_ON(!pud_table(pud)); @@ -1553,6 +1563,7 @@ static void unmap_hotplug_p4d_range(pgd_t *pgdp, unsi= gned long addr, static void unmap_hotplug_range(unsigned long addr, unsigned long end, bool free_mapped, struct vmem_altmap *altmap) { + unsigned long start =3D addr; unsigned long next; pgd_t *pgdp, pgd; =20 @@ -1574,6 +1585,9 @@ static void unmap_hotplug_range(unsigned long addr, u= nsigned long end, WARN_ON(!pgd_present(pgd)); unmap_hotplug_p4d_range(pgdp, addr, next, free_mapped, altmap); } while (addr =3D next, addr < end); + + if (!free_mapped) + flush_tlb_kernel_range(start, end); } =20 static void free_empty_pte_table(pmd_t *pmdp, unsigned long addr, --=20 2.30.2