ANDROID: 16K: Emulate mincore() syscall

Collapse 4 4KiB mincore entries to a single 16KiB entry if emulating
a 16KiB page size to userspace.

Note: nr_subpages == 1 when not emulating the page size, which makes the
change a no-op in the normal case.

Bug: 383389337
Bug: 385169772
Test: atest vts_ltp_test_x86_64:syscalls.mincore03_64bit#syscalls.mincore03_64bit
Test: atest vts_ltp_test_x86_64:syscalls.mincore04_64bit#syscalls.mincore04_64bit
Change-Id: I905b6e0863eb47d03afbb3898063997eb4d3e814
Signed-off-by: Kalesh Singh <kaleshsingh@google.com>
This commit is contained in:
Kalesh Singh
2025-01-16 23:43:05 +00:00
committed by Carlos Llamas
parent 596774b15c
commit 8aab407984

View File

@@ -18,6 +18,7 @@
#include <linux/shmem_fs.h>
#include <linux/hugetlb.h>
#include <linux/pgtable.h>
#include <linux/page_size_compat.h>
#include <linux/uaccess.h>
#include "swap.h"
@@ -205,6 +206,20 @@ static long do_mincore(unsigned long addr, unsigned long pages, unsigned char *v
return (end - addr) >> PAGE_SHIFT;
}
static inline void __collapse_mincore_result(unsigned char *src_vec,
unsigned char *res_vec,
unsigned long pages,
unsigned long nr_subpages)
{
unsigned long i;
if (nr_subpages == 1)
return;
for (i = 0; i < pages; i++)
res_vec[i / nr_subpages] |= src_vec[i];
}
/*
* The mincore(2) system call.
*
@@ -235,11 +250,13 @@ SYSCALL_DEFINE3(mincore, unsigned long, start, size_t, len,
long retval;
unsigned long pages;
unsigned char *tmp;
unsigned char *res;
unsigned long nr_subpages = __PAGE_SIZE / PAGE_SIZE;
start = untagged_addr(start);
/* Check the start address: needs to be page-aligned.. */
if (start & ~PAGE_MASK)
if (start & ~__PAGE_MASK)
return -EINVAL;
/* ..and we need to be passed a valid user-space range */
@@ -250,13 +267,22 @@ SYSCALL_DEFINE3(mincore, unsigned long, start, size_t, len,
pages = len >> PAGE_SHIFT;
pages += (offset_in_page(len)) != 0;
if (!access_ok(vec, pages))
if (!access_ok(vec, pages / nr_subpages))
return -EFAULT;
tmp = (void *) __get_free_page(GFP_USER);
if (!tmp)
return -EAGAIN;
if (unlikely(nr_subpages > 1)) {
res = (void *) __get_free_page(GFP_USER|__GFP_ZERO);
if (!res) {
free_page((unsigned long) tmp);
return -EAGAIN;
}
} else
res = tmp;
retval = 0;
while (pages) {
/*
@@ -269,15 +295,20 @@ SYSCALL_DEFINE3(mincore, unsigned long, start, size_t, len,
if (retval <= 0)
break;
if (copy_to_user(vec, tmp, retval)) {
__collapse_mincore_result(tmp, res, retval, nr_subpages);
if (copy_to_user(vec, res, retval / nr_subpages)) {
retval = -EFAULT;
break;
}
pages -= retval;
vec += retval;
vec += retval / nr_subpages;
start += retval << PAGE_SHIFT;
retval = 0;
}
if (unlikely(nr_subpages > 1))
free_page((unsigned long) res);
free_page((unsigned long) tmp);
return retval;
}