Re: vm_area at addr ffffffffc0800000 is not marked as VM_IOREMAP

Alexei Starovoitov
2024-03-08T17:20:42Z

[-- Attachment #1: Type: text/plain, Size: 1381 bytes --]

On Fri, Mar 8, 2024 at 9:02 AM Christoph Hellwig <hch@infradead.org> wrote:
>
> On Fri, Mar 08, 2024 at 08:33:18AM -0800, Alexei Starovoitov wrote:
> > vmap_range_noflush() is static in mm/vmalloc.c
> > There is vmap_pages_range_noflush() that is in mm/internal.h,
> > but it needs pages instead of phys_addr_t.
> > Newly introduced vm_area_map_pages() needs struct vm_struct *area
> > and struct page **pages.
> > In this PCI case there is no vm_struct and no pages.
> > ioremap_page_range() is the only api that fits. afaict.
>
> Except that we want to enforce a vm_area with the ioremap flag for
> ioremap_page_range, and just adding a NULL check defeats that.
>
> The right long term thing would be to actually create a vm_area
> for the PCI_IOBASE region, but until then we just need a lower level
> API. That's why I suggest to add a vmap_range() that is basically
> the ioremap_page_range before you added the checks, and make
> ioremap_page_range a wrapper around that that checks the area.

ok. Like the attached patch?

> If/when we get PCI_IOBASE handling converted to the proper
> vmalloc/ioremap areas we can remove that again as well as
> vunmap_range which is just used for PCI_IOBASE and other equivalent
> ISA_IO_BASE in powerpc and somewhat unusual case in arm64 that I
> need to look into a bit more.

The plan makes sense to me.

[-- Attachment #2: 0001-mm-Introduce-vmap_page_range-to-be-used-by-PCI.patch --]
[-- Type: application/octet-stream, Size: 3479 bytes --]

From a3a26c8619f6ed6d8cca58ac1b1d548948bfee05 Mon Sep 17 00:00:00 2001
From: Alexei Starovoitov <ast@kernel.org>
Date: Fri, 8 Mar 2024 09:12:54 -0800
Subject: [PATCH bpf-next] mm: Introduce vmap_page_range() to be used by PCI

ioremap_page_range() should be used for ranges within vmalloc range only.
The vmalloc ranges are allocated by get_vm_area(). PCI has "resource"
allocator that manages PCI_IOBASE, IO_SPACE_LIMIT address range, hence
introduce vmap_page_range() to be used exclusively by PCI.

Signed-off-by: Alexei Starovoitov <ast@kernel.org>
---
drivers/pci/pci.c | 4 ++--
include/linux/io.h | 7 +++++++
mm/vmalloc.c | 23 +++++++++++++++--------
3 files changed, 24 insertions(+), 10 deletions(-)

diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index c3585229c12a..ccee56615f78 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -4353,8 +4353,8 @@ int pci_remap_iospace(const struct resource *res, phys_addr_t phys_addr)
if (res->end > IO_SPACE_LIMIT)
return -EINVAL;

- return ioremap_page_range(vaddr, vaddr + resource_size(res), phys_addr,
- pgprot_device(PAGE_KERNEL));
+ return vmap_page_range(vaddr, vaddr + resource_size(res), phys_addr,
+ pgprot_device(PAGE_KERNEL));
#else
/*
* This architecture does not have memory mapped I/O space,
diff --git a/include/linux/io.h b/include/linux/io.h
index 7304f2a69960..235ba7d80a8f 100644
--- a/include/linux/io.h
+++ b/include/linux/io.h
@@ -23,12 +23,19 @@ void __iowrite64_copy(void __iomem *to, const void *from, size_t count);
#ifdef CONFIG_MMU
int ioremap_page_range(unsigned long addr, unsigned long end,
phys_addr_t phys_addr, pgprot_t prot);
+int vmap_page_range(unsigned long addr, unsigned long end,
+ phys_addr_t phys_addr, pgprot_t prot);
#else
static inline int ioremap_page_range(unsigned long addr, unsigned long end,
phys_addr_t phys_addr, pgprot_t prot)
{
return 0;
}
+static inline int vmap_page_range(unsigned long addr, unsigned long end,
+ phys_addr_t phys_addr, pgprot_t prot)
+{
+ return 0;
+}
#endif

/*
diff --git a/mm/vmalloc.c b/mm/vmalloc.c
index e5b8c70950bc..1e36322d83d8 100644
--- a/mm/vmalloc.c
+++ b/mm/vmalloc.c
@@ -304,11 +304,24 @@ static int vmap_range_noflush(unsigned long addr, unsigned long end,
return err;
}

+int vmap_page_range(unsigned long addr, unsigned long end,
+ phys_addr_t phys_addr, pgprot_t prot)
+{
+ int err;
+
+ err = vmap_range_noflush(addr, end, phys_addr, pgprot_nx(prot),
+ ioremap_max_page_shift);
+ flush_cache_vmap(addr, end);
+ if (!err)
+ err = kmsan_ioremap_page_range(addr, end, phys_addr, prot,
+ ioremap_max_page_shift);
+ return err;
+}
+
int ioremap_page_range(unsigned long addr, unsigned long end,
phys_addr_t phys_addr, pgprot_t prot)
{
struct vm_struct *area;
- int err;

area = find_vm_area((void *)addr);
if (!area || !(area->flags & VM_IOREMAP)) {
@@ -322,13 +335,7 @@ int ioremap_page_range(unsigned long addr, unsigned long end,
(long)area->addr + get_vm_area_size(area));
return -ERANGE;
}
- err = vmap_range_noflush(addr, end, phys_addr, pgprot_nx(prot),
- ioremap_max_page_shift);
- flush_cache_vmap(addr, end);
- if (!err)
- err = kmsan_ioremap_page_range(addr, end, phys_addr, prot,
- ioremap_max_page_shift);
- return err;
+ return vmap_page_range(addr, end, phys_addr, prot);
}

static void vunmap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end,
--
2.43.0