[PATCH 2/2] arm64: mm: always map fixmap at page granularity
Ryan Roberts
ryan.roberts at arm.com
Tue Mar 21 07:34:15 PDT 2023
On 21/03/2023 14:18, Mark Rutland wrote:
> On Wed, Mar 15, 2023 at 10:23:31AM +0000, Ryan Roberts wrote:
>> On 14/03/2023 14:21, Mark Rutland wrote:
>
>>> diff --git a/arch/arm64/include/asm/fixmap.h b/arch/arm64/include/asm/fixmap.h
>>> index c153f069e9c9..c59d433c1801 100644
>>> --- a/arch/arm64/include/asm/fixmap.h
>>> +++ b/arch/arm64/include/asm/fixmap.h
>>> @@ -36,17 +36,13 @@ enum fixed_addresses {
>>> FIX_HOLE,
>>>
>>> /*
>>> - * Reserve a virtual window for the FDT that is 2 MB larger than the
>>> - * maximum supported size, and put it at the top of the fixmap region.
>>> - * The additional space ensures that any FDT that does not exceed
>>> - * MAX_FDT_SIZE can be mapped regardless of whether it crosses any
>>> - * 2 MB alignment boundaries.
>>> - *
>>> - * Keep this at the top so it remains 2 MB aligned.
>>> + * Reserve a virtual window for the FDT that is a page bigger than the
>>> + * maximum supported size. The additional space ensures that any FDT
>>> + * that does not exceed MAX_FDT_SIZE can be mapped regardless of
>>> + * whether it crosses any page boundary.
>>> */
>>> -#define FIX_FDT_SIZE (MAX_FDT_SIZE + SZ_2M)
>>> FIX_FDT_END,
>>> - FIX_FDT = FIX_FDT_END + FIX_FDT_SIZE / PAGE_SIZE - 1,
>>> + FIX_FDT = FIX_FDT_END + MAX_FDT_SIZE / PAGE_SIZE - 1,
>>
>> I don't think this is consistent with your comment - needs a +1 for the extra
>> page? On a 4K system, FIX_FDT will be calculated as 512, but we need it to be
>> 513 to deal with the case where the FDT is 2MB and not aligned to a page boundary.
>
> Indeed; the intent was to have 513 in that case, but I clearly either forgot to
> fix the calculation or messed that up when rebasing. I've fixed that to:
>
> FIX_FDT_END + DIV_ROUND_UP(MAX_FDT_SIZE, PAGE_SIZE) + 1,
>
> ... which'll work even if MAX_FDT_SIZE weren't a multiple of PAGE_SIZE.
>
> [...]
>
>>> diff --git a/arch/arm64/mm/fixmap.c b/arch/arm64/mm/fixmap.c
>>> index 54e50552bfe3..d7a6a485f361 100644
>>> --- a/arch/arm64/mm/fixmap.c
>>> +++ b/arch/arm64/mm/fixmap.c
>>> @@ -16,34 +16,77 @@
>>> #include <asm/pgalloc.h>
>>> #include <asm/tlbflush.h>
>>>
>>> -static pte_t bm_pte[PTRS_PER_PTE] __page_aligned_bss;
>>> +#define NR_BM_PTE_TABLES \
>>> + SPAN_NR_ENTRIES(FIXADDR_START, FIXADDR_TOP, PMD_SHIFT)
>>> +#define NR_BM_PMD_TABLES \
>>> + SPAN_NR_ENTRIES(FIXADDR_START, FIXADDR_TOP, PUD_SHIFT)
>>
>> One of the bear traps I stepped in last week when I was wrestling with this is
>> that FIXADDR_START isn't actually the address of the first slot! It is
>> __end_of_permanent_fixed_addresses. But you then have all the FIX_BTMAP slots,
>> FIX_PTE/PMD/PUD/PGD before FIXADDR_START. So I think some refactoring is in
>> order to properly cover all the slots.
>
> Ugh, yes. This is a total mess.
>
>> Honestly, the fact that the fixed_addresses enum is backwards is mind-bending
>> too.
>
> That has been a long standing bugbear of mine, too.
>
>> Any chance we can just define FIXADDR_START and FIXADDR_TOP to cover the
>> whole lot, then calculate the slot address as (FIXADDR_START + (<ENUM> <<
>> PAGE_SHIFT))?
>
> Unfortunately, some code is (implicitly) relying on FIXADDR_START..FIXADDR_TOP
> *not* including the FIX_BTMAP slots (e.g. virt_to_fix() and fix_to_virt() use
> that to implicitly catch dodgy usage). Other code really wants FIXADDR_START to
> be the base of the whole fixmap region (e.g. ptdump).
So perhaps one approach would be to have 2 sets of macros:
#define ARCH_FIXADDR_START
#define ARCH_FIXADDR_TOP
#define FIXADDR_START
#define FIXADDR_TOP
static_assert(ARCH_FIXADDR_START <= FIXADDR_START);
static_assert(ARCH_FIXADDR_TOP >= FIXADDR_TOP);
ARCH_FIXADDR_* covers the entire region exactly. And FIXADDR_* describes the
sub-region that the generic code cares about. Then all the fixmap internals can
be done against ARCH_FIXADDR_* and the generic code can do its checks against
FIXADDR_* ?
>
> Cleaning that up is going to be a bit churny/painful. I'll take a look.
>
>> (plus reorder of start/end enum labels for FDT and BTMAP). Or if
>> we really care about reclaiming the virtual address space between
>> __end_of_permanent_fixed_addresses and __end_of_fixed_addresses, create a new
>> FIXADDR_BOOT_START or whatever.
>
> I don't think we care about reclaiming the address space; it's a few pages at
> best. I'll go take another look at how churny it'd be clean this up...
>
>>> +
>>> +static_assert(NR_BM_PMD_TABLES == 1);
>>> +
>>> +#define __BM_TABLE_IDX(addr, shift) \
>>> + (((addr) >> (shift)) - (FIXADDR_START >> (shift)))
>>
>> Doesn't this run the risk of being negative for one of the slots below
>> FIXADDR_START?
>
> Yes; I had erroneously thought FIXADDR_START was below all of those, and the
> intent was to use the base of the entire fixmap. So as above we'll need to do
> some more preparatory cleanup...
>
>>> +void __init early_fixmap_init_pte(pmd_t *pmdp, unsigned long addr)
>>
>> static? - same comment for pmd/pud below.
>
> Yup; done.
>
>>> +{
>>> + pmd_t pmd = READ_ONCE(*pmdp);
>>> + pte_t *ptep;
>>>
>>> - return pud_offset_kimg(p4dp, addr);
>>> + if (pmd_none(pmd)) {
>>> + ptep = bm_pte[BM_PTE_TABLE_IDX(addr)];;
>>
>> nit: remove one of the semi-colons.
>
> Whoops; rebase error; done.
>
> Thanks,
> Mark.
More information about the linux-arm-kernel
mailing list