Commit 157efa29 authored by Matthew Wilcox (Oracle)'s avatar Matthew Wilcox (Oracle) Committed by Andrew Morton

sh: implement the new page table range API

Add PFN_PTE_SHIFT, update_mmu_cache_range(), flush_dcache_folio() and
flush_icache_pages().  Change the PG_dcache_clean flag from being per-page
to per-folio.  Flush the entire folio containing the pages in
flush_icache_pages() for ease of implementation.

Link: https://lkml.kernel.org/r/20230802151406.3735276-25-willy@infradead.orgSigned-off-by: default avatarMatthew Wilcox (Oracle) <willy@infradead.org>
Acked-by: default avatarMike Rapoport (IBM) <rppt@kernel.org>
Cc: Yoshinori Sato <ysato@users.sourceforge.jp>
Cc: Rich Felker <dalias@libc.org>
Cc: John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
parent 843f9310
...@@ -13,9 +13,9 @@ ...@@ -13,9 +13,9 @@
* - flush_cache_page(mm, vmaddr, pfn) flushes a single page * - flush_cache_page(mm, vmaddr, pfn) flushes a single page
* - flush_cache_range(vma, start, end) flushes a range of pages * - flush_cache_range(vma, start, end) flushes a range of pages
* *
* - flush_dcache_page(pg) flushes(wback&invalidates) a page for dcache * - flush_dcache_folio(folio) flushes(wback&invalidates) a folio for dcache
* - flush_icache_range(start, end) flushes(invalidates) a range for icache * - flush_icache_range(start, end) flushes(invalidates) a range for icache
* - flush_icache_page(vma, pg) flushes(invalidates) a page for icache * - flush_icache_pages(vma, pg, nr) flushes(invalidates) pages for icache
* - flush_cache_sigtramp(vaddr) flushes the signal trampoline * - flush_cache_sigtramp(vaddr) flushes the signal trampoline
*/ */
extern void (*local_flush_cache_all)(void *args); extern void (*local_flush_cache_all)(void *args);
...@@ -23,9 +23,9 @@ extern void (*local_flush_cache_mm)(void *args); ...@@ -23,9 +23,9 @@ extern void (*local_flush_cache_mm)(void *args);
extern void (*local_flush_cache_dup_mm)(void *args); extern void (*local_flush_cache_dup_mm)(void *args);
extern void (*local_flush_cache_page)(void *args); extern void (*local_flush_cache_page)(void *args);
extern void (*local_flush_cache_range)(void *args); extern void (*local_flush_cache_range)(void *args);
extern void (*local_flush_dcache_page)(void *args); extern void (*local_flush_dcache_folio)(void *args);
extern void (*local_flush_icache_range)(void *args); extern void (*local_flush_icache_range)(void *args);
extern void (*local_flush_icache_page)(void *args); extern void (*local_flush_icache_folio)(void *args);
extern void (*local_flush_cache_sigtramp)(void *args); extern void (*local_flush_cache_sigtramp)(void *args);
static inline void cache_noop(void *args) { } static inline void cache_noop(void *args) { }
...@@ -42,11 +42,18 @@ extern void flush_cache_page(struct vm_area_struct *vma, ...@@ -42,11 +42,18 @@ extern void flush_cache_page(struct vm_area_struct *vma,
extern void flush_cache_range(struct vm_area_struct *vma, extern void flush_cache_range(struct vm_area_struct *vma,
unsigned long start, unsigned long end); unsigned long start, unsigned long end);
#define ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE 1 #define ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE 1
void flush_dcache_page(struct page *page); void flush_dcache_folio(struct folio *folio);
#define flush_dcache_folio flush_dcache_folio
static inline void flush_dcache_page(struct page *page)
{
flush_dcache_folio(page_folio(page));
}
extern void flush_icache_range(unsigned long start, unsigned long end); extern void flush_icache_range(unsigned long start, unsigned long end);
#define flush_icache_user_range flush_icache_range #define flush_icache_user_range flush_icache_range
extern void flush_icache_page(struct vm_area_struct *vma, void flush_icache_pages(struct vm_area_struct *vma, struct page *page,
struct page *page); unsigned int nr);
#define flush_icache_page(vma, page) flush_icache_pages(vma, page, 1)
extern void flush_cache_sigtramp(unsigned long address); extern void flush_cache_sigtramp(unsigned long address);
struct flusher_data { struct flusher_data {
......
...@@ -102,13 +102,16 @@ extern void __update_cache(struct vm_area_struct *vma, ...@@ -102,13 +102,16 @@ extern void __update_cache(struct vm_area_struct *vma,
extern void __update_tlb(struct vm_area_struct *vma, extern void __update_tlb(struct vm_area_struct *vma,
unsigned long address, pte_t pte); unsigned long address, pte_t pte);
static inline void static inline void update_mmu_cache_range(struct vm_fault *vmf,
update_mmu_cache(struct vm_area_struct *vma, unsigned long address, pte_t *ptep) struct vm_area_struct *vma, unsigned long address,
pte_t *ptep, unsigned int nr)
{ {
pte_t pte = *ptep; pte_t pte = *ptep;
__update_cache(vma, address, pte); __update_cache(vma, address, pte);
__update_tlb(vma, address, pte); __update_tlb(vma, address, pte);
} }
#define update_mmu_cache(vma, addr, ptep) \
update_mmu_cache_range(NULL, vma, addr, ptep, 1)
extern pgd_t swapper_pg_dir[PTRS_PER_PGD]; extern pgd_t swapper_pg_dir[PTRS_PER_PGD];
extern void paging_init(void); extern void paging_init(void);
......
...@@ -307,14 +307,13 @@ static inline void set_pte(pte_t *ptep, pte_t pte) ...@@ -307,14 +307,13 @@ static inline void set_pte(pte_t *ptep, pte_t pte)
#define set_pte(pteptr, pteval) (*(pteptr) = pteval) #define set_pte(pteptr, pteval) (*(pteptr) = pteval)
#endif #endif
#define set_pte_at(mm,addr,ptep,pteval) set_pte(ptep,pteval)
/* /*
* (pmds are folded into pgds so this doesn't get actually called, * (pmds are folded into pgds so this doesn't get actually called,
* but the define is needed for a generic inline function.) * but the define is needed for a generic inline function.)
*/ */
#define set_pmd(pmdptr, pmdval) (*(pmdptr) = pmdval) #define set_pmd(pmdptr, pmdval) (*(pmdptr) = pmdval)
#define PFN_PTE_SHIFT PAGE_SHIFT
#define pfn_pte(pfn, prot) \ #define pfn_pte(pfn, prot) \
__pte(((unsigned long long)(pfn) << PAGE_SHIFT) | pgprot_val(prot)) __pte(((unsigned long long)(pfn) << PAGE_SHIFT) | pgprot_val(prot))
#define pfn_pmd(pfn, prot) \ #define pfn_pmd(pfn, prot) \
...@@ -323,7 +322,7 @@ static inline void set_pte(pte_t *ptep, pte_t pte) ...@@ -323,7 +322,7 @@ static inline void set_pte(pte_t *ptep, pte_t pte)
#define pte_none(x) (!pte_val(x)) #define pte_none(x) (!pte_val(x))
#define pte_present(x) ((x).pte_low & (_PAGE_PRESENT | _PAGE_PROTNONE)) #define pte_present(x) ((x).pte_low & (_PAGE_PRESENT | _PAGE_PROTNONE))
#define pte_clear(mm,addr,xp) do { set_pte_at(mm, addr, xp, __pte(0)); } while (0) #define pte_clear(mm, addr, ptep) set_pte(ptep, __pte(0))
#define pmd_none(x) (!pmd_val(x)) #define pmd_none(x) (!pmd_val(x))
#define pmd_present(x) (pmd_val(x)) #define pmd_present(x) (pmd_val(x))
......
...@@ -55,9 +55,9 @@ void __init j2_cache_init(void) ...@@ -55,9 +55,9 @@ void __init j2_cache_init(void)
local_flush_cache_dup_mm = j2_flush_both; local_flush_cache_dup_mm = j2_flush_both;
local_flush_cache_page = j2_flush_both; local_flush_cache_page = j2_flush_both;
local_flush_cache_range = j2_flush_both; local_flush_cache_range = j2_flush_both;
local_flush_dcache_page = j2_flush_dcache; local_flush_dcache_folio = j2_flush_dcache;
local_flush_icache_range = j2_flush_icache; local_flush_icache_range = j2_flush_icache;
local_flush_icache_page = j2_flush_icache; local_flush_icache_folio = j2_flush_icache;
local_flush_cache_sigtramp = j2_flush_icache; local_flush_cache_sigtramp = j2_flush_icache;
pr_info("Initial J2 CCR is %.8x\n", __raw_readl(j2_ccr_base)); pr_info("Initial J2 CCR is %.8x\n", __raw_readl(j2_ccr_base));
......
...@@ -107,19 +107,29 @@ static inline void flush_cache_one(unsigned long start, unsigned long phys) ...@@ -107,19 +107,29 @@ static inline void flush_cache_one(unsigned long start, unsigned long phys)
* Write back & invalidate the D-cache of the page. * Write back & invalidate the D-cache of the page.
* (To avoid "alias" issues) * (To avoid "alias" issues)
*/ */
static void sh4_flush_dcache_page(void *arg) static void sh4_flush_dcache_folio(void *arg)
{ {
struct page *page = arg; struct folio *folio = arg;
unsigned long addr = (unsigned long)page_address(page);
#ifndef CONFIG_SMP #ifndef CONFIG_SMP
struct address_space *mapping = page_mapping_file(page); struct address_space *mapping = folio_flush_mapping(folio);
if (mapping && !mapping_mapped(mapping)) if (mapping && !mapping_mapped(mapping))
clear_bit(PG_dcache_clean, &page->flags); clear_bit(PG_dcache_clean, &folio->flags);
else else
#endif #endif
{
unsigned long pfn = folio_pfn(folio);
unsigned long addr = (unsigned long)folio_address(folio);
unsigned int i, nr = folio_nr_pages(folio);
for (i = 0; i < nr; i++) {
flush_cache_one(CACHE_OC_ADDRESS_ARRAY | flush_cache_one(CACHE_OC_ADDRESS_ARRAY |
(addr & shm_align_mask), page_to_phys(page)); (addr & shm_align_mask),
pfn * PAGE_SIZE);
addr += PAGE_SIZE;
pfn++;
}
}
wmb(); wmb();
} }
...@@ -379,7 +389,7 @@ void __init sh4_cache_init(void) ...@@ -379,7 +389,7 @@ void __init sh4_cache_init(void)
__raw_readl(CCN_PRR)); __raw_readl(CCN_PRR));
local_flush_icache_range = sh4_flush_icache_range; local_flush_icache_range = sh4_flush_icache_range;
local_flush_dcache_page = sh4_flush_dcache_page; local_flush_dcache_folio = sh4_flush_dcache_folio;
local_flush_cache_all = sh4_flush_cache_all; local_flush_cache_all = sh4_flush_cache_all;
local_flush_cache_mm = sh4_flush_cache_mm; local_flush_cache_mm = sh4_flush_cache_mm;
local_flush_cache_dup_mm = sh4_flush_cache_mm; local_flush_cache_dup_mm = sh4_flush_cache_mm;
......
...@@ -132,15 +132,20 @@ static void __flush_dcache_page(unsigned long phys) ...@@ -132,15 +132,20 @@ static void __flush_dcache_page(unsigned long phys)
* Write back & invalidate the D-cache of the page. * Write back & invalidate the D-cache of the page.
* (To avoid "alias" issues) * (To avoid "alias" issues)
*/ */
static void sh7705_flush_dcache_page(void *arg) static void sh7705_flush_dcache_folio(void *arg)
{ {
struct page *page = arg; struct folio *folio = arg;
struct address_space *mapping = page_mapping_file(page); struct address_space *mapping = folio_flush_mapping(folio);
if (mapping && !mapping_mapped(mapping)) if (mapping && !mapping_mapped(mapping))
clear_bit(PG_dcache_clean, &page->flags); clear_bit(PG_dcache_clean, &folio->flags);
else else {
__flush_dcache_page(__pa(page_address(page))); unsigned long pfn = folio_pfn(folio);
unsigned int i, nr = folio_nr_pages(folio);
for (i = 0; i < nr; i++)
__flush_dcache_page((pfn + i) * PAGE_SIZE);
}
} }
static void sh7705_flush_cache_all(void *args) static void sh7705_flush_cache_all(void *args)
...@@ -176,19 +181,20 @@ static void sh7705_flush_cache_page(void *args) ...@@ -176,19 +181,20 @@ static void sh7705_flush_cache_page(void *args)
* Not entirely sure why this is necessary on SH3 with 32K cache but * Not entirely sure why this is necessary on SH3 with 32K cache but
* without it we get occasional "Memory fault" when loading a program. * without it we get occasional "Memory fault" when loading a program.
*/ */
static void sh7705_flush_icache_page(void *page) static void sh7705_flush_icache_folio(void *arg)
{ {
__flush_purge_region(page_address(page), PAGE_SIZE); struct folio *folio = arg;
__flush_purge_region(folio_address(folio), folio_size(folio));
} }
void __init sh7705_cache_init(void) void __init sh7705_cache_init(void)
{ {
local_flush_icache_range = sh7705_flush_icache_range; local_flush_icache_range = sh7705_flush_icache_range;
local_flush_dcache_page = sh7705_flush_dcache_page; local_flush_dcache_folio = sh7705_flush_dcache_folio;
local_flush_cache_all = sh7705_flush_cache_all; local_flush_cache_all = sh7705_flush_cache_all;
local_flush_cache_mm = sh7705_flush_cache_all; local_flush_cache_mm = sh7705_flush_cache_all;
local_flush_cache_dup_mm = sh7705_flush_cache_all; local_flush_cache_dup_mm = sh7705_flush_cache_all;
local_flush_cache_range = sh7705_flush_cache_all; local_flush_cache_range = sh7705_flush_cache_all;
local_flush_cache_page = sh7705_flush_cache_page; local_flush_cache_page = sh7705_flush_cache_page;
local_flush_icache_page = sh7705_flush_icache_page; local_flush_icache_folio = sh7705_flush_icache_folio;
} }
...@@ -20,9 +20,9 @@ void (*local_flush_cache_mm)(void *args) = cache_noop; ...@@ -20,9 +20,9 @@ void (*local_flush_cache_mm)(void *args) = cache_noop;
void (*local_flush_cache_dup_mm)(void *args) = cache_noop; void (*local_flush_cache_dup_mm)(void *args) = cache_noop;
void (*local_flush_cache_page)(void *args) = cache_noop; void (*local_flush_cache_page)(void *args) = cache_noop;
void (*local_flush_cache_range)(void *args) = cache_noop; void (*local_flush_cache_range)(void *args) = cache_noop;
void (*local_flush_dcache_page)(void *args) = cache_noop; void (*local_flush_dcache_folio)(void *args) = cache_noop;
void (*local_flush_icache_range)(void *args) = cache_noop; void (*local_flush_icache_range)(void *args) = cache_noop;
void (*local_flush_icache_page)(void *args) = cache_noop; void (*local_flush_icache_folio)(void *args) = cache_noop;
void (*local_flush_cache_sigtramp)(void *args) = cache_noop; void (*local_flush_cache_sigtramp)(void *args) = cache_noop;
void (*__flush_wback_region)(void *start, int size); void (*__flush_wback_region)(void *start, int size);
...@@ -61,15 +61,17 @@ void copy_to_user_page(struct vm_area_struct *vma, struct page *page, ...@@ -61,15 +61,17 @@ void copy_to_user_page(struct vm_area_struct *vma, struct page *page,
unsigned long vaddr, void *dst, const void *src, unsigned long vaddr, void *dst, const void *src,
unsigned long len) unsigned long len)
{ {
if (boot_cpu_data.dcache.n_aliases && page_mapcount(page) && struct folio *folio = page_folio(page);
test_bit(PG_dcache_clean, &page->flags)) {
if (boot_cpu_data.dcache.n_aliases && folio_mapped(folio) &&
test_bit(PG_dcache_clean, &folio->flags)) {
void *vto = kmap_coherent(page, vaddr) + (vaddr & ~PAGE_MASK); void *vto = kmap_coherent(page, vaddr) + (vaddr & ~PAGE_MASK);
memcpy(vto, src, len); memcpy(vto, src, len);
kunmap_coherent(vto); kunmap_coherent(vto);
} else { } else {
memcpy(dst, src, len); memcpy(dst, src, len);
if (boot_cpu_data.dcache.n_aliases) if (boot_cpu_data.dcache.n_aliases)
clear_bit(PG_dcache_clean, &page->flags); clear_bit(PG_dcache_clean, &folio->flags);
} }
if (vma->vm_flags & VM_EXEC) if (vma->vm_flags & VM_EXEC)
...@@ -80,27 +82,30 @@ void copy_from_user_page(struct vm_area_struct *vma, struct page *page, ...@@ -80,27 +82,30 @@ void copy_from_user_page(struct vm_area_struct *vma, struct page *page,
unsigned long vaddr, void *dst, const void *src, unsigned long vaddr, void *dst, const void *src,
unsigned long len) unsigned long len)
{ {
struct folio *folio = page_folio(page);
if (boot_cpu_data.dcache.n_aliases && page_mapcount(page) && if (boot_cpu_data.dcache.n_aliases && page_mapcount(page) &&
test_bit(PG_dcache_clean, &page->flags)) { test_bit(PG_dcache_clean, &folio->flags)) {
void *vfrom = kmap_coherent(page, vaddr) + (vaddr & ~PAGE_MASK); void *vfrom = kmap_coherent(page, vaddr) + (vaddr & ~PAGE_MASK);
memcpy(dst, vfrom, len); memcpy(dst, vfrom, len);
kunmap_coherent(vfrom); kunmap_coherent(vfrom);
} else { } else {
memcpy(dst, src, len); memcpy(dst, src, len);
if (boot_cpu_data.dcache.n_aliases) if (boot_cpu_data.dcache.n_aliases)
clear_bit(PG_dcache_clean, &page->flags); clear_bit(PG_dcache_clean, &folio->flags);
} }
} }
void copy_user_highpage(struct page *to, struct page *from, void copy_user_highpage(struct page *to, struct page *from,
unsigned long vaddr, struct vm_area_struct *vma) unsigned long vaddr, struct vm_area_struct *vma)
{ {
struct folio *src = page_folio(from);
void *vfrom, *vto; void *vfrom, *vto;
vto = kmap_atomic(to); vto = kmap_atomic(to);
if (boot_cpu_data.dcache.n_aliases && page_mapcount(from) && if (boot_cpu_data.dcache.n_aliases && folio_mapped(src) &&
test_bit(PG_dcache_clean, &from->flags)) { test_bit(PG_dcache_clean, &src->flags)) {
vfrom = kmap_coherent(from, vaddr); vfrom = kmap_coherent(from, vaddr);
copy_page(vto, vfrom); copy_page(vto, vfrom);
kunmap_coherent(vfrom); kunmap_coherent(vfrom);
...@@ -136,27 +141,28 @@ EXPORT_SYMBOL(clear_user_highpage); ...@@ -136,27 +141,28 @@ EXPORT_SYMBOL(clear_user_highpage);
void __update_cache(struct vm_area_struct *vma, void __update_cache(struct vm_area_struct *vma,
unsigned long address, pte_t pte) unsigned long address, pte_t pte)
{ {
struct page *page;
unsigned long pfn = pte_pfn(pte); unsigned long pfn = pte_pfn(pte);
if (!boot_cpu_data.dcache.n_aliases) if (!boot_cpu_data.dcache.n_aliases)
return; return;
page = pfn_to_page(pfn);
if (pfn_valid(pfn)) { if (pfn_valid(pfn)) {
int dirty = !test_and_set_bit(PG_dcache_clean, &page->flags); struct folio *folio = page_folio(pfn_to_page(pfn));
int dirty = !test_and_set_bit(PG_dcache_clean, &folio->flags);
if (dirty) if (dirty)
__flush_purge_region(page_address(page), PAGE_SIZE); __flush_purge_region(folio_address(folio),
folio_size(folio));
} }
} }
void __flush_anon_page(struct page *page, unsigned long vmaddr) void __flush_anon_page(struct page *page, unsigned long vmaddr)
{ {
struct folio *folio = page_folio(page);
unsigned long addr = (unsigned long) page_address(page); unsigned long addr = (unsigned long) page_address(page);
if (pages_do_alias(addr, vmaddr)) { if (pages_do_alias(addr, vmaddr)) {
if (boot_cpu_data.dcache.n_aliases && page_mapcount(page) && if (boot_cpu_data.dcache.n_aliases && folio_mapped(folio) &&
test_bit(PG_dcache_clean, &page->flags)) { test_bit(PG_dcache_clean, &folio->flags)) {
void *kaddr; void *kaddr;
kaddr = kmap_coherent(page, vmaddr); kaddr = kmap_coherent(page, vmaddr);
...@@ -164,7 +170,8 @@ void __flush_anon_page(struct page *page, unsigned long vmaddr) ...@@ -164,7 +170,8 @@ void __flush_anon_page(struct page *page, unsigned long vmaddr)
/* __flush_purge_region((void *)kaddr, PAGE_SIZE); */ /* __flush_purge_region((void *)kaddr, PAGE_SIZE); */
kunmap_coherent(kaddr); kunmap_coherent(kaddr);
} else } else
__flush_purge_region((void *)addr, PAGE_SIZE); __flush_purge_region(folio_address(folio),
folio_size(folio));
} }
} }
...@@ -215,11 +222,11 @@ void flush_cache_range(struct vm_area_struct *vma, unsigned long start, ...@@ -215,11 +222,11 @@ void flush_cache_range(struct vm_area_struct *vma, unsigned long start,
} }
EXPORT_SYMBOL(flush_cache_range); EXPORT_SYMBOL(flush_cache_range);
void flush_dcache_page(struct page *page) void flush_dcache_folio(struct folio *folio)
{ {
cacheop_on_each_cpu(local_flush_dcache_page, page, 1); cacheop_on_each_cpu(local_flush_dcache_folio, folio, 1);
} }
EXPORT_SYMBOL(flush_dcache_page); EXPORT_SYMBOL(flush_dcache_folio);
void flush_icache_range(unsigned long start, unsigned long end) void flush_icache_range(unsigned long start, unsigned long end)
{ {
...@@ -233,10 +240,11 @@ void flush_icache_range(unsigned long start, unsigned long end) ...@@ -233,10 +240,11 @@ void flush_icache_range(unsigned long start, unsigned long end)
} }
EXPORT_SYMBOL(flush_icache_range); EXPORT_SYMBOL(flush_icache_range);
void flush_icache_page(struct vm_area_struct *vma, struct page *page) void flush_icache_pages(struct vm_area_struct *vma, struct page *page,
unsigned int nr)
{ {
/* Nothing uses the VMA, so just pass the struct page along */ /* Nothing uses the VMA, so just pass the folio along */
cacheop_on_each_cpu(local_flush_icache_page, page, 1); cacheop_on_each_cpu(local_flush_icache_folio, page_folio(page), 1);
} }
void flush_cache_sigtramp(unsigned long address) void flush_cache_sigtramp(unsigned long address)
......
...@@ -27,10 +27,11 @@ void __init kmap_coherent_init(void) ...@@ -27,10 +27,11 @@ void __init kmap_coherent_init(void)
void *kmap_coherent(struct page *page, unsigned long addr) void *kmap_coherent(struct page *page, unsigned long addr)
{ {
struct folio *folio = page_folio(page);
enum fixed_addresses idx; enum fixed_addresses idx;
unsigned long vaddr; unsigned long vaddr;
BUG_ON(!test_bit(PG_dcache_clean, &page->flags)); BUG_ON(!test_bit(PG_dcache_clean, &folio->flags));
preempt_disable(); preempt_disable();
pagefault_disable(); pagefault_disable();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment