blob: ccbcd6b9fc04165f878c3c0b9758f90b07a3747d [file] [log] [blame]
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __LINUX_PAGE_SIZE_COMPAT_H
#define __LINUX_PAGE_SIZE_COMPAT_H
/*
* include/linux/page_size_compat.h
*
* Page Size Emulation
*
* Copyright (c) 2024, Google LLC.
* Author: Kalesh Singh <[email protected]>
* Helper macros for page size emulation.
*
* The macros for use with the emulated page size are all
* namespaced by the prefix '__'.
*
* The valid range of androidboot.page_shift is [13, 16].
* In other words page sizes of 8KB, 16KB, 32KB and 64KB can
* be emulated.
*/
#include <asm/page.h>
#define __MAX_PAGE_SHIFT 14
#define __MAX_PAGE_SIZE (_AC(1,UL) << __MAX_PAGE_SHIFT)
#define __MAX_PAGE_MASK (~(__MAX_PAGE_SIZE-1))
#ifndef __ASSEMBLY__
#include <linux/align.h>
#include <linux/jump_label.h>
#include <linux/mman.h>
#include <linux/printk.h>
#include <linux/sched.h>
#define pgcompat_err(fmt, ...) \
pr_err("pgcompat [%i (%s)]: " fmt, task_pid_nr(current), current->comm, ## __VA_ARGS__)
DECLARE_STATIC_KEY_FALSE(page_shift_compat_enabled);
extern int page_shift_compat;
#ifdef CONFIG_SHMEM
extern vm_fault_t shmem_fault(struct vm_fault *vmf);
#endif /* CONFIG_SHMEM */
#ifdef CONFIG_F2FS_FS
extern vm_fault_t f2fs_filemap_fault(struct vm_fault *vmf);
#endif /* CONFIG_F2FS_FS */
#ifdef CONFIG_X86_64
static __always_inline unsigned __page_shift(void)
{
if (static_branch_unlikely(&page_shift_compat_enabled))
return page_shift_compat;
else
return PAGE_SHIFT;
}
#else /* !CONFIG_X86_64 */
#define __page_shift() PAGE_SHIFT
#endif /* CONFIG_X86_64 */
#define __PAGE_SHIFT __page_shift()
#define __PAGE_SIZE (_AC(1,UL) << __PAGE_SHIFT)
#define __PAGE_MASK (~(__PAGE_SIZE-1))
#define __PAGE_ALIGN(addr) ALIGN(addr, __PAGE_SIZE)
#define __PAGE_ALIGN_DOWN(addr) ALIGN_DOWN(addr, __PAGE_SIZE)
#define __offset_in_page(p) ((unsigned long)(p) & ~__PAGE_MASK)
#define __offset_in_page_log(addr) \
({ \
if (static_branch_unlikely(&page_shift_compat_enabled) && \
__offset_in_page(addr)) \
pgcompat_err("%s: addr (0x%08lx) not page aligned", __func__, addr); \
(__offset_in_page(addr)); \
})
#define __PAGE_ALIGNED(addr) (!__offset_in_page_log(addr))
/*
* Increases @size by an adequate amount to allow __PAGE_SIZE alignment
* by rounding up; given that @size is already a multiple of the
* base page size (PAGE_SIZE).
*
* Example:
* If __PAGE_SHIFT == PAGE_SHIFT == 12
* @size is increased by 0
* ((1 << (0)) - 1) << PAGE_SHIFT
* (1 ) - 1) << PAGE_SHIFT
* (0 ) << PAGE_SHIFT
*
* If __PAGE_SHIFT == 13 and PAGE_SHIFT == 12
* @size is increased by PAGE_SIZE (4KB):
* ((1 << (1)) - 1) << PAGE_SHIFT
* (2 ) - 1) << PAGE_SHIFT
* (1 ) << PAGE_SHIFT
* If __PAGE_SHIFT == 14 and PAGE_SHIFT == 12
* @size is increased by 3xPAGE_SIZE (12KB):
* ((1 << (2)) - 1) << PAGE_SHIFT
* (4 ) - 1) << PAGE_SHIFT
* (3 ) << PAGE_SHIFT
* ...
*/
#define __PAGE_SIZE_ROUND_UP_ADJ(size) \
((size) + (((1 << (__PAGE_SHIFT - PAGE_SHIFT)) - 1) << PAGE_SHIFT))
/*
* VMA is exempt from emulated page align requirements
*
* NOTE: __MAP_NO_COMPAT is not new UABI it is only ever set by the kernel
* in ___filemap_fixup()
*/
#define __VM_NO_COMPAT _BITULL(58)
#define __MAP_NO_COMPAT _BITUL(31)
/*
* Conditional page-alignment based on mmap flags
*
* If the VMA is allowed to not respect the emulated page size, align using the
* base PAGE_SIZE, else align using the emulated __PAGE_SIZE.
*/
#define __COMPAT_PAGE_ALIGN(size, flags) \
(flags & __MAP_NO_COMPAT) ? PAGE_ALIGN(size) : __PAGE_ALIGN(size)
/*
* Combines the mmap "flags" argument into "vm_flags"
*
* If page size emulation is enabled, adds translation of the no-compat flag.
*/
static __always_inline unsigned long calc_vm_flag_bits(unsigned long flags)
{
unsigned long flag_bits = __calc_vm_flag_bits(flags);
if (static_branch_unlikely(&page_shift_compat_enabled))
flag_bits |= _calc_vm_trans(flags, __MAP_NO_COMPAT, __VM_NO_COMPAT );
return flag_bits;
}
extern unsigned long ___filemap_len(struct inode *inode, unsigned long pgoff,
unsigned long len, unsigned long flags);
extern void ___filemap_fixup(unsigned long addr, unsigned long prot, unsigned long file_backed_len,
unsigned long len);
static __always_inline unsigned long __filemap_len(struct inode *inode, unsigned long pgoff,
unsigned long len, unsigned long flags)
{
if (static_branch_unlikely(&page_shift_compat_enabled))
return ___filemap_len(inode, pgoff, len, flags);
else
return len;
}
static __always_inline void __filemap_fixup(unsigned long addr, unsigned long prot,
unsigned long file_backed_len, unsigned long len)
{
if (static_branch_unlikely(&page_shift_compat_enabled))
___filemap_fixup(addr, prot, file_backed_len, len);
}
extern void __fold_filemap_fixup_entry(struct vma_iterator *iter, unsigned long *end);
extern int __fixup_swap_header(struct file *swap_file, struct address_space *mapping);
#endif /* !__ASSEMBLY__ */
#endif /* __LINUX_PAGE_SIZE_COMPAT_H */