mirror of
https://github.com/torvalds/linux.git
synced 2026-04-21 08:13:56 -04:00
Pull non-MM updates from Andrew Morton:
- "panic: sys_info: Refactor and fix a potential issue" (Andy Shevchenko)
fixes a build issue and does some cleanup in ib/sys_info.c
- "Implement mul_u64_u64_div_u64_roundup()" (David Laight)
enhances the 64-bit math code on behalf of a PWM driver and beefs up
the test module for these library functions
- "scripts/gdb/symbols: make BPF debug info available to GDB" (Ilya Leoshkevich)
makes BPF symbol names, sizes, and line numbers available to the GDB
debugger
- "Enable hung_task and lockup cases to dump system info on demand" (Feng Tang)
adds a sysctl which can be used to cause additional info dumping when
the hung-task and lockup detectors fire
- "lib/base64: add generic encoder/decoder, migrate users" (Kuan-Wei Chiu)
adds a general base64 encoder/decoder to lib/ and migrates several
users away from their private implementations
- "rbree: inline rb_first() and rb_last()" (Eric Dumazet)
makes TCP a little faster
- "liveupdate: Rework KHO for in-kernel users" (Pasha Tatashin)
reworks the KEXEC Handover interfaces in preparation for Live Update
Orchestrator (LUO), and possibly for other future clients
- "kho: simplify state machine and enable dynamic updates" (Pasha Tatashin)
increases the flexibility of KEXEC Handover. Also preparation for LUO
- "Live Update Orchestrator" (Pasha Tatashin)
is a major new feature targeted at cloud environments. Quoting the
cover letter:
This series introduces the Live Update Orchestrator, a kernel
subsystem designed to facilitate live kernel updates using a
kexec-based reboot. This capability is critical for cloud
environments, allowing hypervisors to be updated with minimal
downtime for running virtual machines. LUO achieves this by
preserving the state of selected resources, such as memory,
devices and their dependencies, across the kernel transition.
As a key feature, this series includes support for preserving
memfd file descriptors, which allows critical in-memory data, such
as guest RAM or any other large memory region, to be maintained in
RAM across the kexec reboot.
Mike Rappaport merits a mention here, for his extensive review and
testing work.
- "kexec: reorganize kexec and kdump sysfs" (Sourabh Jain)
moves the kexec and kdump sysfs entries from /sys/kernel/ to
/sys/kernel/kexec/ and adds back-compatibility symlinks which can
hopefully be removed one day
- "kho: fixes for vmalloc restoration" (Mike Rapoport)
fixes a BUG which was being hit during KHO restoration of vmalloc()
regions
* tag 'mm-nonmm-stable-2025-12-06-11-14' of git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm: (139 commits)
calibrate: update header inclusion
Reinstate "resource: avoid unnecessary lookups in find_next_iomem_res()"
vmcoreinfo: track and log recoverable hardware errors
kho: fix restoring of contiguous ranges of order-0 pages
kho: kho_restore_vmalloc: fix initialization of pages array
MAINTAINERS: TPM DEVICE DRIVER: update the W-tag
init: replace simple_strtoul with kstrtoul to improve lpj_setup
KHO: fix boot failure due to kmemleak access to non-PRESENT pages
Documentation/ABI: new kexec and kdump sysfs interface
Documentation/ABI: mark old kexec sysfs deprecated
kexec: move sysfs entries to /sys/kernel/kexec
test_kho: always print restore status
kho: free chunks using free_page() instead of kfree()
selftests/liveupdate: add kexec test for multiple and empty sessions
selftests/liveupdate: add simple kexec-based selftest for LUO
selftests/liveupdate: add userspace API selftests
docs: add documentation for memfd preservation via LUO
mm: memfd_luo: allow preserving memfd
liveupdate: luo_file: add private argument to store runtime state
mm: shmem: export some functions to internal.h
...
261 lines
5.9 KiB
C
261 lines
5.9 KiB
C
#define __SANE_USERSPACE_TYPES__ // Use ll64
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <dirent.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <pthread.h>
|
|
#include <assert.h>
|
|
#include <mm/gup_test.h>
|
|
#include "kselftest.h"
|
|
#include "vm_util.h"
|
|
|
|
#define MB (1UL << 20)
|
|
|
|
/* Just the flags we need, copied from the kernel internals. */
|
|
#define FOLL_WRITE 0x01 /* check pte is writable */
|
|
|
|
#define GUP_TEST_FILE "/sys/kernel/debug/gup_test"
|
|
|
|
static unsigned long cmd = GUP_FAST_BENCHMARK;
|
|
static int gup_fd, repeats = 1;
|
|
static unsigned long size = 128 * MB;
|
|
/* Serialize prints */
|
|
static pthread_mutex_t print_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
|
|
static char *cmd_to_str(unsigned long cmd)
|
|
{
|
|
switch (cmd) {
|
|
case GUP_FAST_BENCHMARK:
|
|
return "GUP_FAST_BENCHMARK";
|
|
case PIN_FAST_BENCHMARK:
|
|
return "PIN_FAST_BENCHMARK";
|
|
case PIN_LONGTERM_BENCHMARK:
|
|
return "PIN_LONGTERM_BENCHMARK";
|
|
case GUP_BASIC_TEST:
|
|
return "GUP_BASIC_TEST";
|
|
case PIN_BASIC_TEST:
|
|
return "PIN_BASIC_TEST";
|
|
case DUMP_USER_PAGES_TEST:
|
|
return "DUMP_USER_PAGES_TEST";
|
|
}
|
|
return "Unknown command";
|
|
}
|
|
|
|
void *gup_thread(void *data)
|
|
{
|
|
struct gup_test gup = *(struct gup_test *)data;
|
|
int i, status;
|
|
|
|
/* Only report timing information on the *_BENCHMARK commands: */
|
|
if ((cmd == PIN_FAST_BENCHMARK) || (cmd == GUP_FAST_BENCHMARK) ||
|
|
(cmd == PIN_LONGTERM_BENCHMARK)) {
|
|
for (i = 0; i < repeats; i++) {
|
|
gup.size = size;
|
|
status = ioctl(gup_fd, cmd, &gup);
|
|
if (status)
|
|
break;
|
|
|
|
pthread_mutex_lock(&print_mutex);
|
|
ksft_print_msg("%s: Time: get:%lld put:%lld us",
|
|
cmd_to_str(cmd), gup.get_delta_usec,
|
|
gup.put_delta_usec);
|
|
if (gup.size != size)
|
|
ksft_print_msg(", truncated (size: %lld)", gup.size);
|
|
ksft_print_msg("\n");
|
|
pthread_mutex_unlock(&print_mutex);
|
|
}
|
|
} else {
|
|
gup.size = size;
|
|
status = ioctl(gup_fd, cmd, &gup);
|
|
if (status)
|
|
goto return_;
|
|
|
|
pthread_mutex_lock(&print_mutex);
|
|
ksft_print_msg("%s: done\n", cmd_to_str(cmd));
|
|
if (gup.size != size)
|
|
ksft_print_msg("Truncated (size: %lld)\n", gup.size);
|
|
pthread_mutex_unlock(&print_mutex);
|
|
}
|
|
|
|
return_:
|
|
ksft_test_result(!status, "ioctl status %d\n", status);
|
|
return NULL;
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
struct gup_test gup = { 0 };
|
|
int filed, i, opt, nr_pages = 1, thp = -1, write = 1, nthreads = 1, ret;
|
|
int flags = MAP_PRIVATE;
|
|
char *file = "/dev/zero";
|
|
pthread_t *tid;
|
|
char *p;
|
|
|
|
while ((opt = getopt(argc, argv, "m:r:n:F:f:abcj:tTLUuwWSHpz")) != -1) {
|
|
switch (opt) {
|
|
case 'a':
|
|
cmd = PIN_FAST_BENCHMARK;
|
|
break;
|
|
case 'b':
|
|
cmd = PIN_BASIC_TEST;
|
|
break;
|
|
case 'L':
|
|
cmd = PIN_LONGTERM_BENCHMARK;
|
|
break;
|
|
case 'c':
|
|
cmd = DUMP_USER_PAGES_TEST;
|
|
/*
|
|
* Dump page 0 (index 1). May be overridden later, by
|
|
* user's non-option arguments.
|
|
*
|
|
* .which_pages is zero-based, so that zero can mean "do
|
|
* nothing".
|
|
*/
|
|
gup.which_pages[0] = 1;
|
|
break;
|
|
case 'p':
|
|
/* works only with DUMP_USER_PAGES_TEST */
|
|
gup.test_flags |= GUP_TEST_FLAG_DUMP_PAGES_USE_PIN;
|
|
break;
|
|
case 'F':
|
|
/* strtol, so you can pass flags in hex form */
|
|
gup.gup_flags = strtol(optarg, 0, 0);
|
|
break;
|
|
case 'j':
|
|
nthreads = atoi(optarg);
|
|
break;
|
|
case 'm':
|
|
size = atoi(optarg) * MB;
|
|
break;
|
|
case 'r':
|
|
repeats = atoi(optarg);
|
|
break;
|
|
case 'n':
|
|
nr_pages = atoi(optarg);
|
|
if (nr_pages < 0)
|
|
nr_pages = size / psize();
|
|
break;
|
|
case 't':
|
|
thp = 1;
|
|
break;
|
|
case 'T':
|
|
thp = 0;
|
|
break;
|
|
case 'U':
|
|
cmd = GUP_BASIC_TEST;
|
|
break;
|
|
case 'u':
|
|
cmd = GUP_FAST_BENCHMARK;
|
|
break;
|
|
case 'w':
|
|
write = 1;
|
|
break;
|
|
case 'W':
|
|
write = 0;
|
|
break;
|
|
case 'f':
|
|
file = optarg;
|
|
break;
|
|
case 'S':
|
|
flags &= ~MAP_PRIVATE;
|
|
flags |= MAP_SHARED;
|
|
break;
|
|
case 'H':
|
|
flags |= (MAP_HUGETLB | MAP_ANONYMOUS);
|
|
break;
|
|
default:
|
|
ksft_exit_fail_msg("Wrong argument\n");
|
|
}
|
|
}
|
|
|
|
if (optind < argc) {
|
|
int extra_arg_count = 0;
|
|
/*
|
|
* For example:
|
|
*
|
|
* ./gup_test -c 0 1 0x1001
|
|
*
|
|
* ...to dump pages 0, 1, and 4097
|
|
*/
|
|
|
|
while ((optind < argc) &&
|
|
(extra_arg_count < GUP_TEST_MAX_PAGES_TO_DUMP)) {
|
|
/*
|
|
* Do the 1-based indexing here, so that the user can
|
|
* use normal 0-based indexing on the command line.
|
|
*/
|
|
long page_index = strtol(argv[optind], 0, 0) + 1;
|
|
|
|
gup.which_pages[extra_arg_count] = page_index;
|
|
extra_arg_count++;
|
|
optind++;
|
|
}
|
|
}
|
|
|
|
ksft_print_header();
|
|
ksft_set_plan(nthreads);
|
|
|
|
filed = open(file, O_RDWR|O_CREAT, 0664);
|
|
if (filed < 0)
|
|
ksft_exit_fail_msg("Unable to open %s: %s\n", file, strerror(errno));
|
|
|
|
gup.nr_pages_per_call = nr_pages;
|
|
if (write)
|
|
gup.gup_flags |= FOLL_WRITE;
|
|
|
|
gup_fd = open(GUP_TEST_FILE, O_RDWR);
|
|
if (gup_fd == -1) {
|
|
switch (errno) {
|
|
case EACCES:
|
|
if (getuid())
|
|
ksft_print_msg("Please run this test as root\n");
|
|
break;
|
|
case ENOENT:
|
|
if (opendir("/sys/kernel/debug") == NULL)
|
|
ksft_print_msg("mount debugfs at /sys/kernel/debug\n");
|
|
ksft_print_msg("check if CONFIG_GUP_TEST is enabled in kernel config\n");
|
|
break;
|
|
default:
|
|
ksft_print_msg("failed to open %s: %s\n", GUP_TEST_FILE, strerror(errno));
|
|
break;
|
|
}
|
|
ksft_test_result_skip("Please run this test as root\n");
|
|
ksft_exit_pass();
|
|
}
|
|
|
|
p = mmap(NULL, size, PROT_READ | PROT_WRITE, flags, filed, 0);
|
|
if (p == MAP_FAILED)
|
|
ksft_exit_fail_msg("mmap: %s\n", strerror(errno));
|
|
gup.addr = (unsigned long)p;
|
|
|
|
if (thp == 1)
|
|
madvise(p, size, MADV_HUGEPAGE);
|
|
else if (thp == 0)
|
|
madvise(p, size, MADV_NOHUGEPAGE);
|
|
|
|
/* Fault them in here, from user space. */
|
|
for (; (unsigned long)p < gup.addr + size; p += psize())
|
|
p[0] = 0;
|
|
|
|
tid = malloc(sizeof(pthread_t) * nthreads);
|
|
assert(tid);
|
|
for (i = 0; i < nthreads; i++) {
|
|
ret = pthread_create(&tid[i], NULL, gup_thread, &gup);
|
|
assert(ret == 0);
|
|
}
|
|
for (i = 0; i < nthreads; i++) {
|
|
ret = pthread_join(tid[i], NULL);
|
|
assert(ret == 0);
|
|
}
|
|
|
|
free(tid);
|
|
|
|
ksft_exit_pass();
|
|
}
|