mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-21 16:05:29 -04:00
Merge tag 'mm-hotfixes-stable-2026-05-18-21-07' of git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm
Pull misc fixes from Andrew Morton: "14 hotfixes. 9 are for MM. 10 are cc:stable and the remainder are for post-7.1 issues or aren't deemed suitable for backporting. There's a two-patch MAINTAINERS series from Mike Rapoport which updates us for the new KEXEC/KDUMP/crash/LUO/etc arrangements. And another two-patch series from Muchun Song to fix a couple of memory-hotplug issues. Otherwise singletons, please see the changelogs for details" * tag 'mm-hotfixes-stable-2026-05-18-21-07' of git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm: mm/memory: fix spurious warning when unmapping device-private/exclusive pages mm: fix __vm_normal_page() to handle missing support for pmd_special()/pud_special() drivers/base/memory: fix memory block reference leak in poison accounting mm/memory_hotplug: fix memory block reference leak on remove lib: kunit_iov_iter: fix test fail on powerpc mm/page_alloc: fix initialization of tags of the huge zero folio with init_on_free MAINTAINERS: add kexec@ list to LIVE UPDATE ENTRY MAINTAINERS: add tree for KDUMP and KEXEC selftests/mm: run_vmtests.sh: fix destructive tests invocation scripts/gdb: slab: update field names of struct kmem_cache scripts/gdb: mm: cast untyped symbols in x86_page_ops mm/damon: fix damos_stat tracepoint format for sz_applied mm/damon/sysfs-schemes: call missing mem_cgroup_iter_break() mm/migrate_device: fix spinlock leak in migrate_vma_insert_huge_pmd_page
This commit is contained in:
@@ -13869,6 +13869,7 @@ M: Pratyush Yadav <pratyush@kernel.org>
|
||||
R: Dave Young <ruirui.yang@linux.dev>
|
||||
L: kexec@lists.infradead.org
|
||||
S: Maintained
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/liveupdate/linux.git
|
||||
F: Documentation/admin-guide/kdump/
|
||||
F: fs/proc/vmcore.c
|
||||
F: include/linux/crash_core.h
|
||||
@@ -14186,6 +14187,7 @@ M: Pasha Tatashin <pasha.tatashin@soleen.com>
|
||||
M: Pratyush Yadav <pratyush@kernel.org>
|
||||
L: kexec@lists.infradead.org
|
||||
W: http://kernel.org/pub/linux/utils/kernel/kexec/
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/liveupdate/linux.git
|
||||
F: include/linux/kexec.h
|
||||
F: include/uapi/linux/kexec.h
|
||||
F: kernel/kexec*
|
||||
@@ -14902,6 +14904,7 @@ LIVE UPDATE
|
||||
M: Pasha Tatashin <pasha.tatashin@soleen.com>
|
||||
M: Mike Rapoport <rppt@kernel.org>
|
||||
M: Pratyush Yadav <pratyush@kernel.org>
|
||||
L: kexec@lists.infradead.org
|
||||
L: linux-kernel@vger.kernel.org
|
||||
S: Maintained
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/liveupdate/linux.git
|
||||
|
||||
@@ -33,7 +33,7 @@ struct folio *vma_alloc_zeroed_movable_folio(struct vm_area_struct *vma,
|
||||
unsigned long vaddr);
|
||||
#define vma_alloc_zeroed_movable_folio vma_alloc_zeroed_movable_folio
|
||||
|
||||
bool tag_clear_highpages(struct page *to, int numpages);
|
||||
bool tag_clear_highpages(struct page *to, int numpages, bool clear_pages);
|
||||
#define __HAVE_ARCH_TAG_CLEAR_HIGHPAGES
|
||||
|
||||
#define copy_user_page(to, from, vaddr, pg) copy_page(to, from)
|
||||
|
||||
@@ -1018,7 +1018,7 @@ struct folio *vma_alloc_zeroed_movable_folio(struct vm_area_struct *vma,
|
||||
return vma_alloc_folio(flags, 0, vma, vaddr);
|
||||
}
|
||||
|
||||
bool tag_clear_highpages(struct page *page, int numpages)
|
||||
bool tag_clear_highpages(struct page *page, int numpages, bool clear_pages)
|
||||
{
|
||||
/*
|
||||
* Check if MTE is supported and fall back to clear_highpage().
|
||||
@@ -1026,13 +1026,16 @@ bool tag_clear_highpages(struct page *page, int numpages)
|
||||
* post_alloc_hook() will invoke tag_clear_highpages().
|
||||
*/
|
||||
if (!system_supports_mte())
|
||||
return false;
|
||||
return clear_pages;
|
||||
|
||||
/* Newly allocated pages, shouldn't have been tagged yet */
|
||||
for (int i = 0; i < numpages; i++, page++) {
|
||||
WARN_ON_ONCE(!try_page_mte_tagging(page));
|
||||
mte_zero_clear_page_tags(page_address(page));
|
||||
if (clear_pages)
|
||||
mte_zero_clear_page_tags(page_address(page));
|
||||
else
|
||||
mte_clear_page_tags(page_address(page));
|
||||
set_page_mte_tagged(page);
|
||||
}
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1230,8 +1230,10 @@ void memblk_nr_poison_inc(unsigned long pfn)
|
||||
const unsigned long block_id = pfn_to_block_id(pfn);
|
||||
struct memory_block *mem = find_memory_block_by_id(block_id);
|
||||
|
||||
if (mem)
|
||||
if (mem) {
|
||||
atomic_long_inc(&mem->nr_hwpoison);
|
||||
put_device(&mem->dev);
|
||||
}
|
||||
}
|
||||
|
||||
void memblk_nr_poison_sub(unsigned long pfn, long i)
|
||||
@@ -1239,8 +1241,10 @@ void memblk_nr_poison_sub(unsigned long pfn, long i)
|
||||
const unsigned long block_id = pfn_to_block_id(pfn);
|
||||
struct memory_block *mem = find_memory_block_by_id(block_id);
|
||||
|
||||
if (mem)
|
||||
if (mem) {
|
||||
atomic_long_sub(i, &mem->nr_hwpoison);
|
||||
put_device(&mem->dev);
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned long memblk_nr_poison(struct memory_block *mem)
|
||||
|
||||
@@ -273,11 +273,11 @@ enum {
|
||||
*
|
||||
* %__GFP_ZERO returns a zeroed page on success.
|
||||
*
|
||||
* %__GFP_ZEROTAGS zeroes memory tags at allocation time if the memory itself
|
||||
* is being zeroed (either via __GFP_ZERO or via init_on_alloc, provided that
|
||||
* __GFP_SKIP_ZERO is not set). This flag is intended for optimization: setting
|
||||
* memory tags at the same time as zeroing memory has minimal additional
|
||||
* performance impact.
|
||||
* %__GFP_ZEROTAGS zeroes memory tags at allocation time. Setting memory tags at
|
||||
* the same time as zeroing memory (e.g., with __GFP_ZERO) has minimal
|
||||
* additional performance impact. However, __GFP_ZEROTAGS also zeroes the tags
|
||||
* even if memory is not getting zeroed at allocation time (e.g.,
|
||||
* with init_on_free).
|
||||
*
|
||||
* %__GFP_SKIP_KASAN makes KASAN skip unpoisoning on page allocation.
|
||||
* Used for userspace and vmalloc pages; the latter are unpoisoned by
|
||||
|
||||
@@ -347,10 +347,11 @@ static inline void clear_highpage_kasan_tagged(struct page *page)
|
||||
|
||||
#ifndef __HAVE_ARCH_TAG_CLEAR_HIGHPAGES
|
||||
|
||||
/* Return false to let people know we did not initialize the pages */
|
||||
static inline bool tag_clear_highpages(struct page *page, int numpages)
|
||||
/* Returns true if the caller has to initialize the pages */
|
||||
static inline bool tag_clear_highpages(struct page *page, int numpages,
|
||||
bool clear_pages)
|
||||
{
|
||||
return false;
|
||||
return clear_pages;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -41,7 +41,7 @@ TRACE_EVENT(damos_stat_after_apply_interval,
|
||||
),
|
||||
|
||||
TP_printk("ctx_idx=%u scheme_idx=%u nr_tried=%lu sz_tried=%lu "
|
||||
"nr_applied=%lu sz_tried=%lu sz_ops_filter_passed=%lu "
|
||||
"nr_applied=%lu sz_applied=%lu sz_ops_filter_passed=%lu "
|
||||
"qt_exceeds=%lu nr_snapshots=%lu",
|
||||
__entry->context_idx, __entry->scheme_idx,
|
||||
__entry->nr_tried, __entry->sz_tried,
|
||||
|
||||
@@ -1128,7 +1128,7 @@ static void __init iov_kunit_iter_to_sg_kvec(struct kunit *test)
|
||||
struct kvec kvec;
|
||||
size_t bufsize;
|
||||
|
||||
bufsize = 0x100000;
|
||||
bufsize = 0x200000;
|
||||
iov_kunit_iter_to_sg_init(test, bufsize, false, &data);
|
||||
|
||||
kvec.iov_base = data.buffer;
|
||||
@@ -1146,7 +1146,7 @@ static void __init iov_kunit_iter_to_sg_bvec(struct kunit *test)
|
||||
struct bio_vec *bvec;
|
||||
struct iov_iter iter;
|
||||
|
||||
bufsize = 0x100000;
|
||||
bufsize = 0x200000;
|
||||
iov_kunit_iter_to_sg_init(test, bufsize, false, &data);
|
||||
|
||||
bvec = kunit_kmalloc_array(test, data.npages, sizeof(*bvec),
|
||||
@@ -1173,7 +1173,7 @@ static void __init iov_kunit_iter_to_sg_folioq(struct kunit *test)
|
||||
struct iov_iter iter;
|
||||
size_t bufsize;
|
||||
|
||||
bufsize = 0x100000;
|
||||
bufsize = 0x200000;
|
||||
iov_kunit_iter_to_sg_init(test, bufsize, false, &data);
|
||||
|
||||
folioq = iov_kunit_create_folioq(test);
|
||||
@@ -1190,7 +1190,7 @@ static void __init iov_kunit_iter_to_sg_xarray(struct kunit *test)
|
||||
struct iov_iter iter;
|
||||
size_t bufsize;
|
||||
|
||||
bufsize = 0x100000;
|
||||
bufsize = 0x200000;
|
||||
iov_kunit_iter_to_sg_init(test, bufsize, false, &data);
|
||||
|
||||
xarray = iov_kunit_create_xarray(test);
|
||||
@@ -1206,7 +1206,7 @@ static void __init iov_kunit_iter_to_sg_ubuf(struct kunit *test)
|
||||
struct iov_iter iter;
|
||||
size_t bufsize;
|
||||
|
||||
bufsize = 0x100000;
|
||||
bufsize = 0x200000;
|
||||
iov_kunit_iter_to_sg_init(test, bufsize, true, &data);
|
||||
|
||||
iov_iter_ubuf(&iter, READ, data.ubuf, bufsize);
|
||||
|
||||
@@ -2594,6 +2594,7 @@ static int damon_sysfs_memcg_path_to_id(char *memcg_path, u64 *id)
|
||||
if (damon_sysfs_memcg_path_eq(memcg, path, memcg_path)) {
|
||||
*id = mem_cgroup_id(memcg);
|
||||
found = true;
|
||||
mem_cgroup_iter_break(NULL, memcg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
24
mm/memory.c
24
mm/memory.c
@@ -612,6 +612,21 @@ static void print_bad_page_map(struct vm_area_struct *vma,
|
||||
dump_stack();
|
||||
add_taint(TAINT_BAD_PAGE, LOCKDEP_NOW_UNRELIABLE);
|
||||
}
|
||||
|
||||
static inline bool pgtable_level_has_pxx_special(enum pgtable_level level)
|
||||
{
|
||||
switch (level) {
|
||||
case PGTABLE_LEVEL_PTE:
|
||||
return IS_ENABLED(CONFIG_ARCH_HAS_PTE_SPECIAL);
|
||||
case PGTABLE_LEVEL_PMD:
|
||||
return IS_ENABLED(CONFIG_ARCH_SUPPORTS_PMD_PFNMAP);
|
||||
case PGTABLE_LEVEL_PUD:
|
||||
return IS_ENABLED(CONFIG_ARCH_SUPPORTS_PUD_PFNMAP);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#define print_bad_pte(vma, addr, pte, page) \
|
||||
print_bad_page_map(vma, addr, pte_val(pte), page, PGTABLE_LEVEL_PTE)
|
||||
|
||||
@@ -684,7 +699,7 @@ static inline struct page *__vm_normal_page(struct vm_area_struct *vma,
|
||||
unsigned long addr, unsigned long pfn, bool special,
|
||||
unsigned long long entry, enum pgtable_level level)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_ARCH_HAS_PTE_SPECIAL)) {
|
||||
if (pgtable_level_has_pxx_special(level)) {
|
||||
if (unlikely(special)) {
|
||||
#ifdef CONFIG_FIND_NORMAL_PAGE
|
||||
if (vma->vm_ops && vma->vm_ops->find_normal_page)
|
||||
@@ -699,8 +714,9 @@ static inline struct page *__vm_normal_page(struct vm_area_struct *vma,
|
||||
return NULL;
|
||||
}
|
||||
/*
|
||||
* With CONFIG_ARCH_HAS_PTE_SPECIAL, any special page table
|
||||
* mappings (incl. shared zero folios) are marked accordingly.
|
||||
* With working pte_special()/pmd_special()..., any special page
|
||||
* table mappings (incl. shared zero folios) are marked
|
||||
* accordingly.
|
||||
*/
|
||||
} else {
|
||||
if (unlikely(vma->vm_flags & (VM_PFNMAP | VM_MIXEDMAP))) {
|
||||
@@ -1739,7 +1755,7 @@ static inline int zap_nonpresent_ptes(struct mmu_gather *tlb,
|
||||
* consider uffd-wp bit when zap. For more information,
|
||||
* see zap_install_uffd_wp_if_needed().
|
||||
*/
|
||||
WARN_ON_ONCE(!vma_is_anonymous(vma));
|
||||
WARN_ON_ONCE(!folio_test_anon(folio));
|
||||
rss[mm_counter(folio)]--;
|
||||
folio_remove_rmap_pte(folio, page, vma);
|
||||
folio_put(folio);
|
||||
|
||||
@@ -1422,6 +1422,8 @@ static void remove_memory_blocks_and_altmaps(u64 start, u64 size)
|
||||
|
||||
altmap = mem->altmap;
|
||||
mem->altmap = NULL;
|
||||
/* drop the ref. we got via find_memory_block() */
|
||||
put_device(&mem->dev);
|
||||
|
||||
remove_memory_block_devices(cur_start, memblock_size);
|
||||
|
||||
|
||||
@@ -850,7 +850,7 @@ static int migrate_vma_insert_huge_pmd_page(struct migrate_vma *migrate,
|
||||
ptl = pmd_lock(vma->vm_mm, pmdp);
|
||||
csa_ret = check_stable_address_space(vma->vm_mm);
|
||||
if (csa_ret)
|
||||
goto abort;
|
||||
goto unlock_abort;
|
||||
|
||||
/*
|
||||
* Check for userfaultfd but do not deliver the fault. Instead,
|
||||
|
||||
@@ -1808,9 +1808,9 @@ static inline bool should_skip_init(gfp_t flags)
|
||||
inline void post_alloc_hook(struct page *page, unsigned int order,
|
||||
gfp_t gfp_flags)
|
||||
{
|
||||
const bool zero_tags = gfp_flags & __GFP_ZEROTAGS;
|
||||
bool init = !want_init_on_free() && want_init_on_alloc(gfp_flags) &&
|
||||
!should_skip_init(gfp_flags);
|
||||
bool zero_tags = init && (gfp_flags & __GFP_ZEROTAGS);
|
||||
int i;
|
||||
|
||||
set_page_private(page, 0);
|
||||
@@ -1832,11 +1832,11 @@ inline void post_alloc_hook(struct page *page, unsigned int order,
|
||||
*/
|
||||
|
||||
/*
|
||||
* If memory tags should be zeroed
|
||||
* (which happens only when memory should be initialized as well).
|
||||
* Clearing tags can efficiently clear the memory for us as well, if
|
||||
* required.
|
||||
*/
|
||||
if (zero_tags)
|
||||
init = !tag_clear_highpages(page, 1 << order);
|
||||
init = tag_clear_highpages(page, 1 << order, /* clear_pages= */init);
|
||||
|
||||
if (!should_skip_kasan_unpoison(gfp_flags) &&
|
||||
kasan_unpoison_pages(page, order, init)) {
|
||||
|
||||
@@ -40,11 +40,11 @@ class x86_page_ops():
|
||||
|
||||
self.PAGE_OFFSET = int(gdb.parse_and_eval("page_offset_base"))
|
||||
self.VMEMMAP_START = int(gdb.parse_and_eval("vmemmap_base"))
|
||||
self.PHYS_BASE = int(gdb.parse_and_eval("phys_base"))
|
||||
self.PHYS_BASE = int(gdb.parse_and_eval("(unsigned long) phys_base"))
|
||||
self.START_KERNEL_map = 0xffffffff80000000
|
||||
|
||||
self.KERNEL_START = gdb.parse_and_eval("_text")
|
||||
self.KERNEL_END = gdb.parse_and_eval("_end")
|
||||
self.KERNEL_START = gdb.parse_and_eval("(unsigned long) &_text")
|
||||
self.KERNEL_END = gdb.parse_and_eval("(unsigned long) &_end")
|
||||
|
||||
self.VMALLOC_START = int(gdb.parse_and_eval("vmalloc_base"))
|
||||
if self.VMALLOC_START == 0xffffc90000000000:
|
||||
|
||||
@@ -196,7 +196,7 @@ def slabtrace(alloc, cache_name):
|
||||
|
||||
if target_cache['flags'] & SLAB_STORE_USER:
|
||||
for i in range(0, nr_node_ids):
|
||||
cache_node = target_cache['node'][i]
|
||||
cache_node = target_cache['per_node']['node'][i]
|
||||
if cache_node['nr_slabs']['counter'] == 0:
|
||||
continue
|
||||
process_slab(loc_track, cache_node['partial'], alloc, target_cache)
|
||||
@@ -300,7 +300,7 @@ def slabinfo():
|
||||
nr_free = 0
|
||||
nr_slabs = 0
|
||||
for i in range(0, nr_node_ids):
|
||||
cache_node = cache['node'][i]
|
||||
cache_node = cache['per_node']['node'][i]
|
||||
try:
|
||||
nr_slabs += cache_node['nr_slabs']['counter']
|
||||
nr_objs = int(cache_node['total_objects']['counter'])
|
||||
|
||||
@@ -985,6 +985,56 @@ TEST_F(hmm, migrate)
|
||||
hmm_buffer_free(buffer);
|
||||
}
|
||||
|
||||
/*
|
||||
* Migrate private file memory to device private memory.
|
||||
*/
|
||||
TEST_F(hmm, migrate_file_private)
|
||||
{
|
||||
struct hmm_buffer *buffer;
|
||||
unsigned long npages;
|
||||
unsigned long size;
|
||||
unsigned long i;
|
||||
int *ptr;
|
||||
int ret;
|
||||
int fd;
|
||||
|
||||
npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift;
|
||||
ASSERT_NE(npages, 0);
|
||||
size = npages << self->page_shift;
|
||||
|
||||
fd = hmm_create_file(size);
|
||||
ASSERT_GE(fd, 0);
|
||||
|
||||
buffer = malloc(sizeof(*buffer));
|
||||
ASSERT_NE(buffer, NULL);
|
||||
|
||||
buffer->fd = fd;
|
||||
buffer->size = size;
|
||||
buffer->mirror = malloc(size);
|
||||
ASSERT_NE(buffer->mirror, NULL);
|
||||
|
||||
buffer->ptr = mmap(NULL, size,
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE,
|
||||
buffer->fd, 0);
|
||||
ASSERT_NE(buffer->ptr, MAP_FAILED);
|
||||
|
||||
/* Initialize buffer in system memory. */
|
||||
for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
|
||||
ptr[i] = i;
|
||||
|
||||
/* Migrate memory to device. */
|
||||
ret = hmm_migrate_sys_to_dev(self->fd, buffer, npages);
|
||||
ASSERT_EQ(ret, 0);
|
||||
ASSERT_EQ(buffer->cpages, npages);
|
||||
|
||||
/* Check what the device read. */
|
||||
for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
|
||||
ASSERT_EQ(ptr[i], i);
|
||||
|
||||
hmm_buffer_free(buffer);
|
||||
}
|
||||
|
||||
/*
|
||||
* Migrate anonymous memory to device private memory and fault some of it back
|
||||
* to system memory, then try migrating the resulting mix of system and device
|
||||
|
||||
@@ -103,7 +103,7 @@ RUN_ALL=false
|
||||
RUN_DESTRUCTIVE=false
|
||||
TAP_PREFIX="# "
|
||||
|
||||
while getopts "aht:n" OPT; do
|
||||
while getopts "aht:nd" OPT; do
|
||||
case ${OPT} in
|
||||
"a") RUN_ALL=true ;;
|
||||
"h") usage ;;
|
||||
|
||||
Reference in New Issue
Block a user