* [PATCH] arm64/mm: Avoid direct referencing page table enties in map_range() @ 2024-07-25 9:10 Anshuman Khandual 2024-07-25 10:36 ` Ryan Roberts 0 siblings, 1 reply; 8+ messages in thread From: Anshuman Khandual @ 2024-07-25 9:10 UTC (permalink / raw) To: linux-arm-kernel Cc: Anshuman Khandual, Catalin Marinas, Will Deacon, Ryan Roberts, linux-kernel Like else where in arm64 platform, use WRITE_ONCE() in map_range() while creating page table entries. This avoids referencing page table entries directly. Cc: Catalin Marinas <catalin.marinas@arm.com> Cc: Will Deacon <will@kernel.org> Cc: Ryan Roberts <ryan.roberts@arm.com> Cc: linux-arm-kernel@lists.infradead.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Anshuman Khandual <anshuman.khandual@arm.com> --- arch/arm64/kernel/pi/map_range.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/arm64/kernel/pi/map_range.c b/arch/arm64/kernel/pi/map_range.c index 5410b2cac590..b93b70cdfb62 100644 --- a/arch/arm64/kernel/pi/map_range.c +++ b/arch/arm64/kernel/pi/map_range.c @@ -56,8 +56,8 @@ void __init map_range(u64 *pte, u64 start, u64 end, u64 pa, pgprot_t prot, * table mapping if necessary and recurse. */ if (pte_none(*tbl)) { - *tbl = __pte(__phys_to_pte_val(*pte) | - PMD_TYPE_TABLE | PMD_TABLE_UXN); + WRITE_ONCE(*tbl, __pte(__phys_to_pte_val(*pte) | + PMD_TYPE_TABLE | PMD_TABLE_UXN)); *pte += PTRS_PER_PTE * sizeof(pte_t); } map_range(pte, start, next, pa, prot, level + 1, @@ -79,7 +79,7 @@ void __init map_range(u64 *pte, u64 start, u64 end, u64 pa, pgprot_t prot, protval &= ~PTE_CONT; /* Put down a block or page mapping */ - *tbl = __pte(__phys_to_pte_val(pa) | protval); + WRITE_ONCE(*tbl, __pte(__phys_to_pte_val(pa) | protval)); } pa += next - start; start = next; -- 2.30.2 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH] arm64/mm: Avoid direct referencing page table enties in map_range() 2024-07-25 9:10 [PATCH] arm64/mm: Avoid direct referencing page table enties in map_range() Anshuman Khandual @ 2024-07-25 10:36 ` Ryan Roberts 2024-07-26 11:26 ` Anshuman Khandual 2024-08-01 11:34 ` Will Deacon 0 siblings, 2 replies; 8+ messages in thread From: Ryan Roberts @ 2024-07-25 10:36 UTC (permalink / raw) To: Anshuman Khandual, linux-arm-kernel Cc: Catalin Marinas, Will Deacon, linux-kernel On 25/07/2024 10:10, Anshuman Khandual wrote: > Like else where in arm64 platform, use WRITE_ONCE() in map_range() while > creating page table entries. This avoids referencing page table entries > directly. I could be wrong, but I don't think this code is ever operating on live pgtables? So there is never a potential to race with the HW walker and therefore no need to guarrantee copy atomicity? As long as the correct barriers are placed at the point where you load the pgdir into the TTBRx there should be no problem? If my assertion is correct, I don't think there is any need for this change. Thanks, Ryan > > Cc: Catalin Marinas <catalin.marinas@arm.com> > Cc: Will Deacon <will@kernel.org> > Cc: Ryan Roberts <ryan.roberts@arm.com> > Cc: linux-arm-kernel@lists.infradead.org > Cc: linux-kernel@vger.kernel.org > Signed-off-by: Anshuman Khandual <anshuman.khandual@arm.com> > --- > arch/arm64/kernel/pi/map_range.c | 6 +++--- > 1 file changed, 3 insertions(+), 3 deletions(-) > > diff --git a/arch/arm64/kernel/pi/map_range.c b/arch/arm64/kernel/pi/map_range.c > index 5410b2cac590..b93b70cdfb62 100644 > --- a/arch/arm64/kernel/pi/map_range.c > +++ b/arch/arm64/kernel/pi/map_range.c > @@ -56,8 +56,8 @@ void __init map_range(u64 *pte, u64 start, u64 end, u64 pa, pgprot_t prot, > * table mapping if necessary and recurse. > */ > if (pte_none(*tbl)) { > - *tbl = __pte(__phys_to_pte_val(*pte) | > - PMD_TYPE_TABLE | PMD_TABLE_UXN); > + WRITE_ONCE(*tbl, __pte(__phys_to_pte_val(*pte) | > + PMD_TYPE_TABLE | PMD_TABLE_UXN)); > *pte += PTRS_PER_PTE * sizeof(pte_t); > } > map_range(pte, start, next, pa, prot, level + 1, > @@ -79,7 +79,7 @@ void __init map_range(u64 *pte, u64 start, u64 end, u64 pa, pgprot_t prot, > protval &= ~PTE_CONT; > > /* Put down a block or page mapping */ > - *tbl = __pte(__phys_to_pte_val(pa) | protval); > + WRITE_ONCE(*tbl, __pte(__phys_to_pte_val(pa) | protval)); > } > pa += next - start; > start = next; ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] arm64/mm: Avoid direct referencing page table enties in map_range() 2024-07-25 10:36 ` Ryan Roberts @ 2024-07-26 11:26 ` Anshuman Khandual 2024-08-01 11:34 ` Will Deacon 1 sibling, 0 replies; 8+ messages in thread From: Anshuman Khandual @ 2024-07-26 11:26 UTC (permalink / raw) To: Ryan Roberts, linux-arm-kernel; +Cc: Catalin Marinas, Will Deacon, linux-kernel On 7/25/24 16:06, Ryan Roberts wrote: > On 25/07/2024 10:10, Anshuman Khandual wrote: >> Like else where in arm64 platform, use WRITE_ONCE() in map_range() while >> creating page table entries. This avoids referencing page table entries >> directly. > > I could be wrong, but I don't think this code is ever operating on live map_range() is called on these page tables but sequentially during boot. primary_entry() create_init_idmap() map_range(...init_idmap_pg_dir...) primary_switch() early_map_kernel() map_fdt() map_range(...init_idmap_pg_dir...) remap_idmap_for_lpa2() create_init_idmap() map_range(...init_pg_dir...) create_init_idmap() map_range(...init_idmap_pg_dir...) map_kernel() map_segment() map_range(...init_pg_dir...) paging_init() create_idmap() __pi_map_range(...idmap_pg_dir...) > pgtables? So there is never a potential to race with the HW walker and therefore > no need to guarrantee copy atomicity? As long as the correct barriers are placed Unless there is possibility of concurrent HW walk through these page tables, WRITE_ONCE() based atomic is not required here ? I thought arm64 platform decided some time earlier (but don't remember when) to use READ_ONCE()-WRITE_ONCE() for all page table entry, direct references for read or write accesses - possibly for some increased safety ? > at the point where you load the pgdir into the TTBRx there should be no problem? Those barriers are already placed as required. > > If my assertion is correct, I don't think there is any need for this change. > > Thanks, > Ryan > >> >> Cc: Catalin Marinas <catalin.marinas@arm.com> >> Cc: Will Deacon <will@kernel.org> >> Cc: Ryan Roberts <ryan.roberts@arm.com> >> Cc: linux-arm-kernel@lists.infradead.org >> Cc: linux-kernel@vger.kernel.org >> Signed-off-by: Anshuman Khandual <anshuman.khandual@arm.com> >> --- >> arch/arm64/kernel/pi/map_range.c | 6 +++--- >> 1 file changed, 3 insertions(+), 3 deletions(-) >> >> diff --git a/arch/arm64/kernel/pi/map_range.c b/arch/arm64/kernel/pi/map_range.c >> index 5410b2cac590..b93b70cdfb62 100644 >> --- a/arch/arm64/kernel/pi/map_range.c >> +++ b/arch/arm64/kernel/pi/map_range.c >> @@ -56,8 +56,8 @@ void __init map_range(u64 *pte, u64 start, u64 end, u64 pa, pgprot_t prot, >> * table mapping if necessary and recurse. >> */ >> if (pte_none(*tbl)) { >> - *tbl = __pte(__phys_to_pte_val(*pte) | >> - PMD_TYPE_TABLE | PMD_TABLE_UXN); >> + WRITE_ONCE(*tbl, __pte(__phys_to_pte_val(*pte) | >> + PMD_TYPE_TABLE | PMD_TABLE_UXN)); >> *pte += PTRS_PER_PTE * sizeof(pte_t); >> } >> map_range(pte, start, next, pa, prot, level + 1, >> @@ -79,7 +79,7 @@ void __init map_range(u64 *pte, u64 start, u64 end, u64 pa, pgprot_t prot, >> protval &= ~PTE_CONT; >> >> /* Put down a block or page mapping */ >> - *tbl = __pte(__phys_to_pte_val(pa) | protval); >> + WRITE_ONCE(*tbl, __pte(__phys_to_pte_val(pa) | protval)); >> } >> pa += next - start; >> start = next; > ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] arm64/mm: Avoid direct referencing page table enties in map_range() 2024-07-25 10:36 ` Ryan Roberts 2024-07-26 11:26 ` Anshuman Khandual @ 2024-08-01 11:34 ` Will Deacon 2024-08-01 12:48 ` Ryan Roberts 1 sibling, 1 reply; 8+ messages in thread From: Will Deacon @ 2024-08-01 11:34 UTC (permalink / raw) To: Ryan Roberts Cc: Anshuman Khandual, linux-arm-kernel, Catalin Marinas, linux-kernel On Thu, Jul 25, 2024 at 11:36:56AM +0100, Ryan Roberts wrote: > On 25/07/2024 10:10, Anshuman Khandual wrote: > > Like else where in arm64 platform, use WRITE_ONCE() in map_range() while > > creating page table entries. This avoids referencing page table entries > > directly. > > I could be wrong, but I don't think this code is ever operating on live > pgtables? So there is never a potential to race with the HW walker and therefore > no need to guarrantee copy atomicity? As long as the correct barriers are placed > at the point where you load the pgdir into the TTBRx there should be no problem? > > If my assertion is correct, I don't think there is any need for this change. Agreed. Will ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] arm64/mm: Avoid direct referencing page table enties in map_range() 2024-08-01 11:34 ` Will Deacon @ 2024-08-01 12:48 ` Ryan Roberts 2024-08-01 13:23 ` Will Deacon 0 siblings, 1 reply; 8+ messages in thread From: Ryan Roberts @ 2024-08-01 12:48 UTC (permalink / raw) To: Will Deacon Cc: Anshuman Khandual, linux-arm-kernel, Catalin Marinas, linux-kernel On 01/08/2024 12:34, Will Deacon wrote: > On Thu, Jul 25, 2024 at 11:36:56AM +0100, Ryan Roberts wrote: >> On 25/07/2024 10:10, Anshuman Khandual wrote: >>> Like else where in arm64 platform, use WRITE_ONCE() in map_range() while >>> creating page table entries. This avoids referencing page table entries >>> directly. >> >> I could be wrong, but I don't think this code is ever operating on live >> pgtables? So there is never a potential to race with the HW walker and therefore >> no need to guarrantee copy atomicity? As long as the correct barriers are placed >> at the point where you load the pgdir into the TTBRx there should be no problem? >> >> If my assertion is correct, I don't think there is any need for this change. > > Agreed. I think I need to row back on this. It looks like map_range() does act on live pgtables; see map_kernel() where twopass == true. init_pg_dir is populated then installed in TTBR1, then permissions are modified with 3 [un]map_segment() calls, which call through to map_range(). So on that basis, I think the WRITE_ONCE() calls are warranted. And to be consistent, I'd additionally recommend adding a READ_ONCE() around the: if (pte_none(*tbl)) { Thanks, Ryan > > Will ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] arm64/mm: Avoid direct referencing page table enties in map_range() 2024-08-01 12:48 ` Ryan Roberts @ 2024-08-01 13:23 ` Will Deacon 2024-08-01 14:07 ` Ryan Roberts 0 siblings, 1 reply; 8+ messages in thread From: Will Deacon @ 2024-08-01 13:23 UTC (permalink / raw) To: Ryan Roberts Cc: Anshuman Khandual, linux-arm-kernel, Catalin Marinas, linux-kernel On Thu, Aug 01, 2024 at 01:48:17PM +0100, Ryan Roberts wrote: > On 01/08/2024 12:34, Will Deacon wrote: > > On Thu, Jul 25, 2024 at 11:36:56AM +0100, Ryan Roberts wrote: > >> On 25/07/2024 10:10, Anshuman Khandual wrote: > >>> Like else where in arm64 platform, use WRITE_ONCE() in map_range() while > >>> creating page table entries. This avoids referencing page table entries > >>> directly. > >> > >> I could be wrong, but I don't think this code is ever operating on live > >> pgtables? So there is never a potential to race with the HW walker and therefore > >> no need to guarrantee copy atomicity? As long as the correct barriers are placed > >> at the point where you load the pgdir into the TTBRx there should be no problem? > >> > >> If my assertion is correct, I don't think there is any need for this change. > > > > Agreed. > > I think I need to row back on this. It looks like map_range() does act on live > pgtables; see map_kernel() where twopass == true. init_pg_dir is populated then > installed in TTBR1, then permissions are modified with 3 [un]map_segment() > calls, which call through to map_range(). I think the permission part is fine, but I hadn't spotted that unmap_segment() uses map_range() to zap the ptes mapping the text. That *does* need single-copy atomicity, so should probably use __set_pte_nosync(). > So on that basis, I think the WRITE_ONCE() calls are warranted. And to be > consistent, I'd additionally recommend adding a READ_ONCE() around the: > > if (pte_none(*tbl)) { Why? I don't think we need that. Will ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] arm64/mm: Avoid direct referencing page table enties in map_range() 2024-08-01 13:23 ` Will Deacon @ 2024-08-01 14:07 ` Ryan Roberts 2024-08-01 14:46 ` Will Deacon 0 siblings, 1 reply; 8+ messages in thread From: Ryan Roberts @ 2024-08-01 14:07 UTC (permalink / raw) To: Will Deacon Cc: Anshuman Khandual, linux-arm-kernel, Catalin Marinas, linux-kernel On 01/08/2024 14:23, Will Deacon wrote: > On Thu, Aug 01, 2024 at 01:48:17PM +0100, Ryan Roberts wrote: >> On 01/08/2024 12:34, Will Deacon wrote: >>> On Thu, Jul 25, 2024 at 11:36:56AM +0100, Ryan Roberts wrote: >>>> On 25/07/2024 10:10, Anshuman Khandual wrote: >>>>> Like else where in arm64 platform, use WRITE_ONCE() in map_range() while >>>>> creating page table entries. This avoids referencing page table entries >>>>> directly. >>>> >>>> I could be wrong, but I don't think this code is ever operating on live >>>> pgtables? So there is never a potential to race with the HW walker and therefore >>>> no need to guarrantee copy atomicity? As long as the correct barriers are placed >>>> at the point where you load the pgdir into the TTBRx there should be no problem? >>>> >>>> If my assertion is correct, I don't think there is any need for this change. >>> >>> Agreed. >> >> I think I need to row back on this. It looks like map_range() does act on live >> pgtables; see map_kernel() where twopass == true. init_pg_dir is populated then >> installed in TTBR1, then permissions are modified with 3 [un]map_segment() >> calls, which call through to map_range(). > > I think the permission part is fine, but I hadn't spotted that > unmap_segment() uses map_range() to zap the ptes mapping the text. That > *does* need single-copy atomicity, so should probably use > __set_pte_nosync(). Yes, nice. > >> So on that basis, I think the WRITE_ONCE() calls are warranted. And to be >> consistent, I'd additionally recommend adding a READ_ONCE() around the: >> >> if (pte_none(*tbl)) { > > Why? I don't think we need that. I Agree its not technically required. I was suggesting it just for consistency with the other change. So perhaps __ptep_get()? diff --git a/arch/arm64/kernel/pi/map_range.c b/arch/arm64/kernel/pi/map_range.c index 5410b2cac5907..3f6c5717ff782 100644 --- a/arch/arm64/kernel/pi/map_range.c +++ b/arch/arm64/kernel/pi/map_range.c @@ -55,13 +55,14 @@ void __init map_range(u64 *pte, u64 start, u64 end, u64 pa, pgprot_t prot, * This chunk needs a finer grained mapping. Create a * table mapping if necessary and recurse. */ - if (pte_none(*tbl)) { - *tbl = __pte(__phys_to_pte_val(*pte) | - PMD_TYPE_TABLE | PMD_TABLE_UXN); + if (pte_none(__ptep_get(tbl))) { + __set_pte_nosync(tbl, + __pte(__phys_to_pte_val(*pte) | + PMD_TYPE_TABLE | PMD_TABLE_UXN)); *pte += PTRS_PER_PTE * sizeof(pte_t); } map_range(pte, start, next, pa, prot, level + 1, - (pte_t *)(__pte_to_phys(*tbl) + va_offset), + (pte_t *)(__pte_to_phys(__ptep_get(tbl)) + va_offset), may_use_cont, va_offset); } else { /* @@ -79,7 +80,7 @@ void __init map_range(u64 *pte, u64 start, u64 end, u64 pa, pgprot_t prot, protval &= ~PTE_CONT; /* Put down a block or page mapping */ - *tbl = __pte(__phys_to_pte_val(pa) | protval); + __set_pte_nosync(tbl, __pte(__phys_to_pte_val(pa) | protval)); } pa += next - start; start = next; > > Will ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH] arm64/mm: Avoid direct referencing page table enties in map_range() 2024-08-01 14:07 ` Ryan Roberts @ 2024-08-01 14:46 ` Will Deacon 0 siblings, 0 replies; 8+ messages in thread From: Will Deacon @ 2024-08-01 14:46 UTC (permalink / raw) To: Ryan Roberts Cc: Anshuman Khandual, linux-arm-kernel, Catalin Marinas, linux-kernel On Thu, Aug 01, 2024 at 03:07:31PM +0100, Ryan Roberts wrote: > diff --git a/arch/arm64/kernel/pi/map_range.c b/arch/arm64/kernel/pi/map_range.c > index 5410b2cac5907..3f6c5717ff782 100644 > --- a/arch/arm64/kernel/pi/map_range.c > +++ b/arch/arm64/kernel/pi/map_range.c > @@ -55,13 +55,14 @@ void __init map_range(u64 *pte, u64 start, u64 end, u64 pa, pgprot_t prot, > * This chunk needs a finer grained mapping. Create a > * table mapping if necessary and recurse. > */ > - if (pte_none(*tbl)) { > - *tbl = __pte(__phys_to_pte_val(*pte) | > - PMD_TYPE_TABLE | PMD_TABLE_UXN); > + if (pte_none(__ptep_get(tbl))) { > + __set_pte_nosync(tbl, > + __pte(__phys_to_pte_val(*pte) | > + PMD_TYPE_TABLE | PMD_TABLE_UXN)); > *pte += PTRS_PER_PTE * sizeof(pte_t); > } > map_range(pte, start, next, pa, prot, level + 1, > - (pte_t *)(__pte_to_phys(*tbl) + va_offset), > + (pte_t *)(__pte_to_phys(__ptep_get(tbl)) + va_offset), > may_use_cont, va_offset); > } else { > /* > @@ -79,7 +80,7 @@ void __init map_range(u64 *pte, u64 start, u64 end, u64 pa, pgprot_t prot, > protval &= ~PTE_CONT; > > /* Put down a block or page mapping */ > - *tbl = __pte(__phys_to_pte_val(pa) | protval); > + __set_pte_nosync(tbl, __pte(__phys_to_pte_val(pa) | protval)); > } > pa += next - start; > start = next; That looks good to me! Will ^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2024-08-01 14:46 UTC | newest] Thread overview: 8+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2024-07-25 9:10 [PATCH] arm64/mm: Avoid direct referencing page table enties in map_range() Anshuman Khandual 2024-07-25 10:36 ` Ryan Roberts 2024-07-26 11:26 ` Anshuman Khandual 2024-08-01 11:34 ` Will Deacon 2024-08-01 12:48 ` Ryan Roberts 2024-08-01 13:23 ` Will Deacon 2024-08-01 14:07 ` Ryan Roberts 2024-08-01 14:46 ` Will Deacon
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox