RE: [PATCH] swiotlb: use the adjusted address for the highmem page lookup

From: Michael Kelley

Date: Sat Sep 05 2026 - 16:07:50 EST


From: Donggeun Yoo <donggeunyoo.kernel@xxxxxxxxx> Sent: Saturday, September 5, 2026 11:55 AM
>
> On Sat, Sep 05, 2026 at 03:45:41PM +0000, Michael Kelley wrote:
> > I don't understand this paragraph, but that may be because I'm not that
> > familiar with highmem. Are all the slots making up a particular swiotlb
> > mapping either highmem or lowmem? If a mixture is possible, then a
> > partial sync could start somewhere in a lowmem page and cross over
> > into a highmem page, which would break.
>
> The slots are always lowmem: the pool comes from memblock_alloc_low() and
> swiotlb holds one kernel address for it in mem->vaddr. PageHighMem() here
> asks about the pages behind orig_addr, so the question is whether one
> mapping's original buffer can span both.

Yes, that was exactly my question. I worded it poorly.

> It can. ZONE_NORMAL ends at
> max_low_pfn and ZONE_HIGHMEM starts there, and while the page allocator
> will not hand out a run across that line, pages_are_mergeable() and
> bvec_try_merge_page() merge on physical adjacency alone.

So there's exactly one case, which is when a merged range crosses
the boundary between lowmem and highmem. And the range in
that case always starts with lowmem and transitions to highmem.
There's not a case where lowmem and highmem are intermixed
in a range in arbitrary ways. And there's no case that starts with
highmem and transitions to lowmem (though that would just work
with the current test).

>
> Your case is real, and this patch does not fix it. On a 32-bit ARM guest
> (multi_v7_defconfig plus ARM_LPAE and HIGHMEM, 2G, swiotlb=force) with
> lowmem ending at pfn 0x70000 and high_memory at f0000000:
>
> orig=6ffffe00 len=1024 last=700001ff
> mainline PageHighMem(pfn_to_page(PFN_DOWN(orig))) = 0
> this patch PhysHighMem(orig) = 0
> phys_to_virt(last) = f00001ff, past high_memory
>
> dma_map_page(pfn 0x6ffff, off 3584, len 1024, TO_DEVICE)
> Unable to handle kernel paging request at virtual address f0000000
> Internal error: Oops: 206 [#1] SMP ARM
> PC is at mmiocpy+0x4c/0x334
> dma_map_page_attrs from ...
>
> The same oops with and without this patch, and no partial sync is needed
> for it: the bounce at map time does it.

OK, yes. That makes sense.

>
> is_highmem() is monotonic in the pfn, since ZONE_HIGHMEM and a
> ZONE_MOVABLE carved out of it are the highest zones, so testing the last
> byte alone covers your case and this one, at the cost of the single test
> already there. On top of this patch:
>
> - if (PhysHighMem(orig_addr)) {
> + if (PhysHighMem(orig_addr + size - 1)) {
>
> The loop copies through kmap_local_page(), which is fine for a lowmem
> page, so entering it for a range that only ends in highmem is correct.
> That guest survives the map above with it.

Yep. I thought that would be the case. Using kmap_local_page()
is slower, but the case where the range starts with lowmem and
transition to highmem is a rarity. Please leave a comment in the
code about the reasoning behind the strange-looking test.

>
> The second one predates 5f89468e2f06, so I will send it separately.

Works for me. Thanks!

Michael