2019-06-03 07:44:50 +02:00
|
|
|
/* SPDX-License-Identifier: GPL-2.0-only */
|
2012-03-05 11:49:28 +00:00
|
|
|
/*
|
|
|
|
* Based on arch/arm/include/asm/tlb.h
|
|
|
|
*
|
|
|
|
* Copyright (C) 2002 Russell King
|
|
|
|
* Copyright (C) 2012 ARM Ltd.
|
|
|
|
*/
|
|
|
|
#ifndef __ASM_TLB_H
|
|
|
|
#define __ASM_TLB_H
|
|
|
|
|
2014-10-09 15:29:23 -07:00
|
|
|
#include <linux/pagemap.h>
|
|
|
|
|
|
|
|
|
2018-09-04 13:18:15 +02:00
|
|
|
#define tlb_flush tlb_flush
|
2018-08-24 00:23:04 +01:00
|
|
|
static void tlb_flush(struct mmu_gather *tlb);
|
|
|
|
|
2014-10-29 10:03:09 +00:00
|
|
|
#include <asm-generic/tlb.h>
|
|
|
|
|
2020-06-25 16:03:13 +08:00
|
|
|
/*
|
arm64/mm: Update tlb invalidation routines for FEAT_LPA2
FEAT_LPA2 impacts tlb invalidation in 2 ways; Firstly, the TTL field in
the non-range tlbi instructions can now validly take a 0 value as a
level hint for the 4KB granule (this is due to the extra level of
translation) - previously TTL=0b0100 meant no hint and was treated as
0b0000. Secondly, The BADDR field of the range-based tlbi instructions
is specified in 64KB units when LPA2 is in use (TCR.DS=1), whereas it is
in page units otherwise. Changes are required for tlbi to continue to
operate correctly when LPA2 is in use.
Solve the first problem by always adding the level hint if the level is
between [0, 3] (previously anything other than 0 was hinted, which
breaks in the new level -1 case from kvm). When running on non-LPA2 HW,
0 is still safe to hint as the HW will fall back to non-hinted. While we
are at it, we replace the notion of 0 being the non-hinted sentinel with
a macro, TLBI_TTL_UNKNOWN. This means callers won't need updating
if/when translation depth increases in future.
The second issue is more complex: When LPA2 is in use, use the non-range
tlbi instructions to forward align to a 64KB boundary first, then use
range-based tlbi from there on, until we have either invalidated all
pages or we have a single page remaining. If the latter, that is done
with non-range tlbi. We determine whether LPA2 is in use based on
lpa2_is_enabled() (for kernel calls) or kvm_lpa2_is_enabled() (for kvm
calls).
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Reviewed-by: Oliver Upton <oliver.upton@linux.dev>
Signed-off-by: Ryan Roberts <ryan.roberts@arm.com>
Signed-off-by: Marc Zyngier <maz@kernel.org>
Link: https://lore.kernel.org/r/20231127111737.1897081-4-ryan.roberts@arm.com
2023-11-27 11:17:28 +00:00
|
|
|
* get the tlbi levels in arm64. Default value is TLBI_TTL_UNKNOWN if more than
|
|
|
|
* one of cleared_* is set or neither is set - this elides the level hinting to
|
|
|
|
* the hardware.
|
2020-06-25 16:03:13 +08:00
|
|
|
*/
|
|
|
|
static inline int tlb_get_level(struct mmu_gather *tlb)
|
|
|
|
{
|
2021-06-23 15:05:22 +08:00
|
|
|
/* The TTL field is only valid for the leaf entry. */
|
|
|
|
if (tlb->freed_tables)
|
arm64/mm: Update tlb invalidation routines for FEAT_LPA2
FEAT_LPA2 impacts tlb invalidation in 2 ways; Firstly, the TTL field in
the non-range tlbi instructions can now validly take a 0 value as a
level hint for the 4KB granule (this is due to the extra level of
translation) - previously TTL=0b0100 meant no hint and was treated as
0b0000. Secondly, The BADDR field of the range-based tlbi instructions
is specified in 64KB units when LPA2 is in use (TCR.DS=1), whereas it is
in page units otherwise. Changes are required for tlbi to continue to
operate correctly when LPA2 is in use.
Solve the first problem by always adding the level hint if the level is
between [0, 3] (previously anything other than 0 was hinted, which
breaks in the new level -1 case from kvm). When running on non-LPA2 HW,
0 is still safe to hint as the HW will fall back to non-hinted. While we
are at it, we replace the notion of 0 being the non-hinted sentinel with
a macro, TLBI_TTL_UNKNOWN. This means callers won't need updating
if/when translation depth increases in future.
The second issue is more complex: When LPA2 is in use, use the non-range
tlbi instructions to forward align to a 64KB boundary first, then use
range-based tlbi from there on, until we have either invalidated all
pages or we have a single page remaining. If the latter, that is done
with non-range tlbi. We determine whether LPA2 is in use based on
lpa2_is_enabled() (for kernel calls) or kvm_lpa2_is_enabled() (for kvm
calls).
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Reviewed-by: Oliver Upton <oliver.upton@linux.dev>
Signed-off-by: Ryan Roberts <ryan.roberts@arm.com>
Signed-off-by: Marc Zyngier <maz@kernel.org>
Link: https://lore.kernel.org/r/20231127111737.1897081-4-ryan.roberts@arm.com
2023-11-27 11:17:28 +00:00
|
|
|
return TLBI_TTL_UNKNOWN;
|
2021-06-23 15:05:22 +08:00
|
|
|
|
2020-06-25 16:03:13 +08:00
|
|
|
if (tlb->cleared_ptes && !(tlb->cleared_pmds ||
|
|
|
|
tlb->cleared_puds ||
|
|
|
|
tlb->cleared_p4ds))
|
|
|
|
return 3;
|
|
|
|
|
|
|
|
if (tlb->cleared_pmds && !(tlb->cleared_ptes ||
|
|
|
|
tlb->cleared_puds ||
|
|
|
|
tlb->cleared_p4ds))
|
|
|
|
return 2;
|
|
|
|
|
|
|
|
if (tlb->cleared_puds && !(tlb->cleared_ptes ||
|
|
|
|
tlb->cleared_pmds ||
|
|
|
|
tlb->cleared_p4ds))
|
|
|
|
return 1;
|
|
|
|
|
arm64/mm: Update tlb invalidation routines for FEAT_LPA2
FEAT_LPA2 impacts tlb invalidation in 2 ways; Firstly, the TTL field in
the non-range tlbi instructions can now validly take a 0 value as a
level hint for the 4KB granule (this is due to the extra level of
translation) - previously TTL=0b0100 meant no hint and was treated as
0b0000. Secondly, The BADDR field of the range-based tlbi instructions
is specified in 64KB units when LPA2 is in use (TCR.DS=1), whereas it is
in page units otherwise. Changes are required for tlbi to continue to
operate correctly when LPA2 is in use.
Solve the first problem by always adding the level hint if the level is
between [0, 3] (previously anything other than 0 was hinted, which
breaks in the new level -1 case from kvm). When running on non-LPA2 HW,
0 is still safe to hint as the HW will fall back to non-hinted. While we
are at it, we replace the notion of 0 being the non-hinted sentinel with
a macro, TLBI_TTL_UNKNOWN. This means callers won't need updating
if/when translation depth increases in future.
The second issue is more complex: When LPA2 is in use, use the non-range
tlbi instructions to forward align to a 64KB boundary first, then use
range-based tlbi from there on, until we have either invalidated all
pages or we have a single page remaining. If the latter, that is done
with non-range tlbi. We determine whether LPA2 is in use based on
lpa2_is_enabled() (for kernel calls) or kvm_lpa2_is_enabled() (for kvm
calls).
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Reviewed-by: Oliver Upton <oliver.upton@linux.dev>
Signed-off-by: Ryan Roberts <ryan.roberts@arm.com>
Signed-off-by: Marc Zyngier <maz@kernel.org>
Link: https://lore.kernel.org/r/20231127111737.1897081-4-ryan.roberts@arm.com
2023-11-27 11:17:28 +00:00
|
|
|
if (tlb->cleared_p4ds && !(tlb->cleared_ptes ||
|
|
|
|
tlb->cleared_pmds ||
|
|
|
|
tlb->cleared_puds))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return TLBI_TTL_UNKNOWN;
|
2020-06-25 16:03:13 +08:00
|
|
|
}
|
|
|
|
|
2012-03-05 11:49:28 +00:00
|
|
|
static inline void tlb_flush(struct mmu_gather *tlb)
|
|
|
|
{
|
mm: do not initialize TLB stack vma's with vma_init()
Commit 2c4541e24c55 ("mm: use vma_init() to initialize VMAs on stack and
data segments") tried to initialize various left-over ad-hoc vma's
"properly", but actually made things worse for the temporary vma's used
for TLB flushing.
vma_init() doesn't actually initialize all of the vma, just a few
fields, so doing something like
- struct vm_area_struct vma = { .vm_mm = tlb->mm, };
+ struct vm_area_struct vma;
+
+ vma_init(&vma, tlb->mm);
was actually very bad: instead of having a nicely initialized vma with
every field but "vm_mm" zeroed, you'd have an entirely uninitialized vma
with only a couple of fields initialized. And they weren't even fields
that the code in question mostly cared about.
The flush_tlb_range() function takes a "struct vma" rather than a
"struct mm_struct", because a few architectures actually care about what
kind of range it is - being able to only do an ITLB flush if it's a
range that doesn't have data accesses enabled, for example. And all the
normal users already have the vma for doing the range invalidation.
But a few people want to call flush_tlb_range() with a range they just
made up, so they also end up using a made-up vma. x86 just has a
special "flush_tlb_mm_range()" function for this, but other
architectures (arm and ia64) do the "use fake vma" thing instead, and
thus got caught up in the vma_init() changes.
At the same time, the TLB flushing code really doesn't care about most
other fields in the vma, so vma_init() is just unnecessary and
pointless.
This fixes things by having an explicit "this is just an initializer for
the TLB flush" initializer macro, which is used by the arm/arm64/ia64
people who mis-use this interface with just a dummy vma.
Fixes: 2c4541e24c55 ("mm: use vma_init() to initialize VMAs on stack and data segments")
Cc: Dmitry Vyukov <dvyukov@google.com>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: Andrea Arcangeli <aarcange@redhat.com>
Cc: Kirill Shutemov <kirill.shutemov@linux.intel.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: John Stultz <john.stultz@linaro.org>
Cc: Hugh Dickins <hughd@google.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2018-08-01 13:43:38 -07:00
|
|
|
struct vm_area_struct vma = TLB_FLUSH_VMA(tlb->mm, 0);
|
2018-08-23 21:08:31 +01:00
|
|
|
bool last_level = !tlb->freed_tables;
|
|
|
|
unsigned long stride = tlb_get_unmap_size(tlb);
|
2020-06-25 16:03:13 +08:00
|
|
|
int tlb_level = tlb_get_level(tlb);
|
2015-10-06 18:46:26 +01:00
|
|
|
|
|
|
|
/*
|
2018-08-23 21:08:31 +01:00
|
|
|
* If we're tearing down the address space then we only care about
|
|
|
|
* invalidating the walk-cache, since the ASID allocator won't
|
|
|
|
* reallocate our ASID without invalidating the entire TLB.
|
2015-10-06 18:46:26 +01:00
|
|
|
*/
|
2018-08-23 21:08:31 +01:00
|
|
|
if (tlb->fullmm) {
|
|
|
|
if (!last_level)
|
|
|
|
flush_tlb_mm(tlb->mm);
|
2015-10-06 18:46:26 +01:00
|
|
|
return;
|
2018-08-23 21:08:31 +01:00
|
|
|
}
|
2015-10-06 18:46:26 +01:00
|
|
|
|
2020-06-25 16:03:13 +08:00
|
|
|
__flush_tlb_range(&vma, tlb->start, tlb->end, stride,
|
|
|
|
last_level, tlb_level);
|
2012-03-05 11:49:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline void __pte_free_tlb(struct mmu_gather *tlb, pgtable_t pte,
|
2014-02-11 15:22:01 +00:00
|
|
|
unsigned long addr)
|
2012-03-05 11:49:28 +00:00
|
|
|
{
|
2023-08-07 16:05:00 -07:00
|
|
|
struct ptdesc *ptdesc = page_ptdesc(pte);
|
|
|
|
|
|
|
|
tlb_remove_ptdesc(tlb, ptdesc);
|
2012-03-05 11:49:28 +00:00
|
|
|
}
|
|
|
|
|
2015-04-14 15:45:39 -07:00
|
|
|
#if CONFIG_PGTABLE_LEVELS > 2
|
2012-03-05 11:49:28 +00:00
|
|
|
static inline void __pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmdp,
|
|
|
|
unsigned long addr)
|
|
|
|
{
|
2023-08-07 16:05:00 -07:00
|
|
|
struct ptdesc *ptdesc = virt_to_ptdesc(pmdp);
|
2019-03-11 18:57:49 -06:00
|
|
|
|
2023-08-07 16:05:00 -07:00
|
|
|
tlb_remove_ptdesc(tlb, ptdesc);
|
2012-03-05 11:49:28 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2015-04-14 15:45:39 -07:00
|
|
|
#if CONFIG_PGTABLE_LEVELS > 3
|
2014-05-12 18:40:51 +09:00
|
|
|
static inline void __pud_free_tlb(struct mmu_gather *tlb, pud_t *pudp,
|
|
|
|
unsigned long addr)
|
|
|
|
{
|
2023-09-18 14:31:42 +08:00
|
|
|
struct ptdesc *ptdesc = virt_to_ptdesc(pudp);
|
|
|
|
|
2024-02-14 13:29:22 +01:00
|
|
|
if (!pgtable_l4_enabled())
|
|
|
|
return;
|
|
|
|
|
2023-09-18 14:31:42 +08:00
|
|
|
tlb_remove_ptdesc(tlb, ptdesc);
|
2014-05-12 18:40:51 +09:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2025-01-08 14:57:21 +08:00
|
|
|
#if CONFIG_PGTABLE_LEVELS > 4
|
|
|
|
static inline void __p4d_free_tlb(struct mmu_gather *tlb, p4d_t *p4dp,
|
|
|
|
unsigned long addr)
|
|
|
|
{
|
|
|
|
struct ptdesc *ptdesc = virt_to_ptdesc(p4dp);
|
|
|
|
|
|
|
|
if (!pgtable_l5_enabled())
|
|
|
|
return;
|
|
|
|
|
|
|
|
tlb_remove_ptdesc(tlb, ptdesc);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2012-03-05 11:49:28 +00:00
|
|
|
#endif
|