mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
Merge branch 'mm-hotfixes-stable' into mm-nonmm-stable in order to be able
to merge "kho: make debugfs interface optional" into mm-nonmm-stable.
This commit is contained in:
@@ -206,6 +206,7 @@ Danilo Krummrich <dakr@kernel.org> <dakr@redhat.com>
|
||||
David Brownell <david-b@pacbell.net>
|
||||
David Collins <quic_collinsd@quicinc.com> <collinsd@codeaurora.org>
|
||||
David Heidelberg <david@ixit.cz> <d.okias@gmail.com>
|
||||
David Hildenbrand <david@kernel.org> <david@redhat.com>
|
||||
David Rheinsberg <david@readahead.eu> <dh.herrmann@gmail.com>
|
||||
David Rheinsberg <david@readahead.eu> <dh.herrmann@googlemail.com>
|
||||
David Rheinsberg <david@readahead.eu> <david.rheinsberg@gmail.com>
|
||||
@@ -687,6 +688,8 @@ Sachin Mokashi <sachin.mokashi@intel.com> <sachinx.mokashi@intel.com>
|
||||
Sachin P Sant <ssant@in.ibm.com>
|
||||
Sai Prakash Ranjan <quic_saipraka@quicinc.com> <saiprakash.ranjan@codeaurora.org>
|
||||
Sakari Ailus <sakari.ailus@linux.intel.com> <sakari.ailus@iki.fi>
|
||||
Sam Protsenko <semen.protsenko@linaro.org>
|
||||
Sam Protsenko <semen.protsenko@linaro.org> <semen.protsenko@globallogic.com>
|
||||
Sam Ravnborg <sam@mars.ravnborg.org>
|
||||
Sankeerth Billakanti <quic_sbillaka@quicinc.com> <sbillaka@codeaurora.org>
|
||||
Santosh Shilimkar <santosh.shilimkar@oracle.org>
|
||||
|
||||
+17
-16
@@ -11526,7 +11526,7 @@ F: include/linux/platform_data/huawei-gaokun-ec.h
|
||||
HUGETLB SUBSYSTEM
|
||||
M: Muchun Song <muchun.song@linux.dev>
|
||||
M: Oscar Salvador <osalvador@suse.de>
|
||||
R: David Hildenbrand <david@redhat.com>
|
||||
R: David Hildenbrand <david@kernel.org>
|
||||
L: linux-mm@kvack.org
|
||||
S: Maintained
|
||||
F: Documentation/ABI/testing/sysfs-kernel-mm-hugepages
|
||||
@@ -13734,7 +13734,7 @@ KERNEL VIRTUAL MACHINE for s390 (KVM/s390)
|
||||
M: Christian Borntraeger <borntraeger@linux.ibm.com>
|
||||
M: Janosch Frank <frankja@linux.ibm.com>
|
||||
M: Claudio Imbrenda <imbrenda@linux.ibm.com>
|
||||
R: David Hildenbrand <david@redhat.com>
|
||||
R: David Hildenbrand <david@kernel.org>
|
||||
L: kvm@vger.kernel.org
|
||||
S: Supported
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/kvms390/linux.git
|
||||
@@ -13800,6 +13800,7 @@ F: Documentation/admin-guide/mm/kho.rst
|
||||
F: Documentation/core-api/kho/*
|
||||
F: include/linux/kexec_handover.h
|
||||
F: kernel/kexec_handover.c
|
||||
F: lib/test_kho.c
|
||||
F: tools/testing/selftests/kho/
|
||||
|
||||
KEYS-ENCRYPTED
|
||||
@@ -16222,7 +16223,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/chanwoo/linux.git
|
||||
F: drivers/devfreq/tegra30-devfreq.c
|
||||
|
||||
MEMORY HOT(UN)PLUG
|
||||
M: David Hildenbrand <david@redhat.com>
|
||||
M: David Hildenbrand <david@kernel.org>
|
||||
M: Oscar Salvador <osalvador@suse.de>
|
||||
L: linux-mm@kvack.org
|
||||
S: Maintained
|
||||
@@ -16247,7 +16248,7 @@ F: tools/mm/
|
||||
|
||||
MEMORY MANAGEMENT - CORE
|
||||
M: Andrew Morton <akpm@linux-foundation.org>
|
||||
M: David Hildenbrand <david@redhat.com>
|
||||
M: David Hildenbrand <david@kernel.org>
|
||||
R: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
|
||||
R: Liam R. Howlett <Liam.Howlett@oracle.com>
|
||||
R: Vlastimil Babka <vbabka@suse.cz>
|
||||
@@ -16303,7 +16304,7 @@ F: mm/execmem.c
|
||||
|
||||
MEMORY MANAGEMENT - GUP (GET USER PAGES)
|
||||
M: Andrew Morton <akpm@linux-foundation.org>
|
||||
M: David Hildenbrand <david@redhat.com>
|
||||
M: David Hildenbrand <david@kernel.org>
|
||||
R: Jason Gunthorpe <jgg@nvidia.com>
|
||||
R: John Hubbard <jhubbard@nvidia.com>
|
||||
R: Peter Xu <peterx@redhat.com>
|
||||
@@ -16319,7 +16320,7 @@ F: tools/testing/selftests/mm/gup_test.c
|
||||
|
||||
MEMORY MANAGEMENT - KSM (Kernel Samepage Merging)
|
||||
M: Andrew Morton <akpm@linux-foundation.org>
|
||||
M: David Hildenbrand <david@redhat.com>
|
||||
M: David Hildenbrand <david@kernel.org>
|
||||
R: Xu Xin <xu.xin16@zte.com.cn>
|
||||
R: Chengming Zhou <chengming.zhou@linux.dev>
|
||||
L: linux-mm@kvack.org
|
||||
@@ -16335,7 +16336,7 @@ F: mm/mm_slot.h
|
||||
|
||||
MEMORY MANAGEMENT - MEMORY POLICY AND MIGRATION
|
||||
M: Andrew Morton <akpm@linux-foundation.org>
|
||||
M: David Hildenbrand <david@redhat.com>
|
||||
M: David Hildenbrand <david@kernel.org>
|
||||
R: Zi Yan <ziy@nvidia.com>
|
||||
R: Matthew Brost <matthew.brost@intel.com>
|
||||
R: Joshua Hahn <joshua.hahnjy@gmail.com>
|
||||
@@ -16375,7 +16376,7 @@ F: mm/workingset.c
|
||||
|
||||
MEMORY MANAGEMENT - MISC
|
||||
M: Andrew Morton <akpm@linux-foundation.org>
|
||||
M: David Hildenbrand <david@redhat.com>
|
||||
M: David Hildenbrand <david@kernel.org>
|
||||
R: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
|
||||
R: Liam R. Howlett <Liam.Howlett@oracle.com>
|
||||
R: Vlastimil Babka <vbabka@suse.cz>
|
||||
@@ -16463,7 +16464,7 @@ F: mm/shuffle.h
|
||||
MEMORY MANAGEMENT - RECLAIM
|
||||
M: Andrew Morton <akpm@linux-foundation.org>
|
||||
M: Johannes Weiner <hannes@cmpxchg.org>
|
||||
R: David Hildenbrand <david@redhat.com>
|
||||
R: David Hildenbrand <david@kernel.org>
|
||||
R: Michal Hocko <mhocko@kernel.org>
|
||||
R: Qi Zheng <zhengqi.arch@bytedance.com>
|
||||
R: Shakeel Butt <shakeel.butt@linux.dev>
|
||||
@@ -16476,7 +16477,7 @@ F: mm/workingset.c
|
||||
|
||||
MEMORY MANAGEMENT - RMAP (REVERSE MAPPING)
|
||||
M: Andrew Morton <akpm@linux-foundation.org>
|
||||
M: David Hildenbrand <david@redhat.com>
|
||||
M: David Hildenbrand <david@kernel.org>
|
||||
M: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
|
||||
R: Rik van Riel <riel@surriel.com>
|
||||
R: Liam R. Howlett <Liam.Howlett@oracle.com>
|
||||
@@ -16500,12 +16501,12 @@ F: mm/secretmem.c
|
||||
|
||||
MEMORY MANAGEMENT - SWAP
|
||||
M: Andrew Morton <akpm@linux-foundation.org>
|
||||
M: Chris Li <chrisl@kernel.org>
|
||||
M: Kairui Song <kasong@tencent.com>
|
||||
R: Kemeng Shi <shikemeng@huaweicloud.com>
|
||||
R: Kairui Song <kasong@tencent.com>
|
||||
R: Nhat Pham <nphamcs@gmail.com>
|
||||
R: Baoquan He <bhe@redhat.com>
|
||||
R: Barry Song <baohua@kernel.org>
|
||||
R: Chris Li <chrisl@kernel.org>
|
||||
L: linux-mm@kvack.org
|
||||
S: Maintained
|
||||
F: Documentation/mm/swap-table.rst
|
||||
@@ -16521,7 +16522,7 @@ F: mm/swapfile.c
|
||||
|
||||
MEMORY MANAGEMENT - THP (TRANSPARENT HUGE PAGE)
|
||||
M: Andrew Morton <akpm@linux-foundation.org>
|
||||
M: David Hildenbrand <david@redhat.com>
|
||||
M: David Hildenbrand <david@kernel.org>
|
||||
M: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
|
||||
R: Zi Yan <ziy@nvidia.com>
|
||||
R: Baolin Wang <baolin.wang@linux.alibaba.com>
|
||||
@@ -16623,7 +16624,7 @@ MEMORY MAPPING - MADVISE (MEMORY ADVICE)
|
||||
M: Andrew Morton <akpm@linux-foundation.org>
|
||||
M: Liam R. Howlett <Liam.Howlett@oracle.com>
|
||||
M: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
|
||||
M: David Hildenbrand <david@redhat.com>
|
||||
M: David Hildenbrand <david@kernel.org>
|
||||
R: Vlastimil Babka <vbabka@suse.cz>
|
||||
R: Jann Horn <jannh@google.com>
|
||||
L: linux-mm@kvack.org
|
||||
@@ -27090,7 +27091,7 @@ F: net/vmw_vsock/virtio_transport_common.c
|
||||
|
||||
VIRTIO BALLOON
|
||||
M: "Michael S. Tsirkin" <mst@redhat.com>
|
||||
M: David Hildenbrand <david@redhat.com>
|
||||
M: David Hildenbrand <david@kernel.org>
|
||||
L: virtualization@lists.linux.dev
|
||||
S: Maintained
|
||||
F: drivers/virtio/virtio_balloon.c
|
||||
@@ -27245,7 +27246,7 @@ F: drivers/iommu/virtio-iommu.c
|
||||
F: include/uapi/linux/virtio_iommu.h
|
||||
|
||||
VIRTIO MEM DRIVER
|
||||
M: David Hildenbrand <david@redhat.com>
|
||||
M: David Hildenbrand <david@kernel.org>
|
||||
L: virtualization@lists.linux.dev
|
||||
S: Maintained
|
||||
W: https://virtio-mem.gitlab.io/
|
||||
|
||||
@@ -476,7 +476,8 @@ static int __access_remote_tags(struct mm_struct *mm, unsigned long addr,
|
||||
|
||||
folio = page_folio(page);
|
||||
if (folio_test_hugetlb(folio))
|
||||
WARN_ON_ONCE(!folio_test_hugetlb_mte_tagged(folio));
|
||||
WARN_ON_ONCE(!folio_test_hugetlb_mte_tagged(folio) &&
|
||||
!is_huge_zero_folio(folio));
|
||||
else
|
||||
WARN_ON_ONCE(!page_mte_tagged(page) && !is_zero_page(page));
|
||||
|
||||
|
||||
@@ -969,6 +969,16 @@ struct folio *vma_alloc_zeroed_movable_folio(struct vm_area_struct *vma,
|
||||
|
||||
void tag_clear_highpage(struct page *page)
|
||||
{
|
||||
/*
|
||||
* Check if MTE is supported and fall back to clear_highpage().
|
||||
* get_huge_zero_folio() unconditionally passes __GFP_ZEROTAGS and
|
||||
* post_alloc_hook() will invoke tag_clear_highpage().
|
||||
*/
|
||||
if (!system_supports_mte()) {
|
||||
clear_highpage(page);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Newly allocated page, shouldn't have been tagged yet */
|
||||
WARN_ON_ONCE(!try_page_mte_tagging(page));
|
||||
mte_zero_clear_page_tags(page_address(page));
|
||||
|
||||
@@ -137,6 +137,7 @@ config PPC
|
||||
select ARCH_HAS_DMA_OPS if PPC64
|
||||
select ARCH_HAS_FORTIFY_SOURCE
|
||||
select ARCH_HAS_GCOV_PROFILE_ALL
|
||||
select ARCH_HAS_GIGANTIC_PAGE if ARCH_SUPPORTS_HUGETLBFS
|
||||
select ARCH_HAS_KCOV
|
||||
select ARCH_HAS_KERNEL_FPU_SUPPORT if PPC64 && PPC_FPU
|
||||
select ARCH_HAS_MEMBARRIER_CALLBACKS
|
||||
|
||||
@@ -423,7 +423,6 @@ config PPC_64S_HASH_MMU
|
||||
config PPC_RADIX_MMU
|
||||
bool "Radix MMU Support"
|
||||
depends on PPC_BOOK3S_64
|
||||
select ARCH_HAS_GIGANTIC_PAGE
|
||||
default y
|
||||
help
|
||||
Enable support for the Power ISA 3.0 Radix style MMU. Currently this
|
||||
|
||||
+6
-1
@@ -2768,7 +2768,12 @@ static void nilfs_segctor_destroy(struct nilfs_sc_info *sci)
|
||||
|
||||
if (sci->sc_task) {
|
||||
wake_up(&sci->sc_wait_daemon);
|
||||
kthread_stop(sci->sc_task);
|
||||
if (kthread_stop(sci->sc_task)) {
|
||||
spin_lock(&sci->sc_state_lock);
|
||||
sci->sc_task = NULL;
|
||||
timer_shutdown_sync(&sci->sc_timer);
|
||||
spin_unlock(&sci->sc_state_lock);
|
||||
}
|
||||
}
|
||||
|
||||
spin_lock(&sci->sc_state_lock);
|
||||
|
||||
+9
-3
@@ -698,6 +698,12 @@ void pde_put(struct proc_dir_entry *pde)
|
||||
}
|
||||
}
|
||||
|
||||
static void pde_erase(struct proc_dir_entry *pde, struct proc_dir_entry *parent)
|
||||
{
|
||||
rb_erase(&pde->subdir_node, &parent->subdir);
|
||||
RB_CLEAR_NODE(&pde->subdir_node);
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove a /proc entry and free it if it's not currently in use.
|
||||
*/
|
||||
@@ -720,7 +726,7 @@ void remove_proc_entry(const char *name, struct proc_dir_entry *parent)
|
||||
WARN(1, "removing permanent /proc entry '%s'", de->name);
|
||||
de = NULL;
|
||||
} else {
|
||||
rb_erase(&de->subdir_node, &parent->subdir);
|
||||
pde_erase(de, parent);
|
||||
if (S_ISDIR(de->mode))
|
||||
parent->nlink--;
|
||||
}
|
||||
@@ -764,7 +770,7 @@ int remove_proc_subtree(const char *name, struct proc_dir_entry *parent)
|
||||
root->parent->name, root->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
rb_erase(&root->subdir_node, &parent->subdir);
|
||||
pde_erase(root, parent);
|
||||
|
||||
de = root;
|
||||
while (1) {
|
||||
@@ -776,7 +782,7 @@ int remove_proc_subtree(const char *name, struct proc_dir_entry *parent)
|
||||
next->parent->name, next->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
rb_erase(&next->subdir_node, &de->subdir);
|
||||
pde_erase(next, de);
|
||||
de = next;
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <linux/mmzone.h>
|
||||
#include <linux/topology.h>
|
||||
#include <linux/alloc_tag.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
struct vm_area_struct;
|
||||
@@ -463,4 +464,6 @@ static inline struct folio *folio_alloc_gigantic_noprof(int order, gfp_t gfp,
|
||||
/* This should be paired with folio_put() rather than free_contig_range(). */
|
||||
#define folio_alloc_gigantic(...) alloc_hooks(folio_alloc_gigantic_noprof(__VA_ARGS__))
|
||||
|
||||
DEFINE_FREE(free_page, void *, free_page((unsigned long)_T))
|
||||
|
||||
#endif /* __LINUX_GFP_H */
|
||||
|
||||
+23
-32
@@ -376,45 +376,30 @@ bool non_uniform_split_supported(struct folio *folio, unsigned int new_order,
|
||||
int folio_split(struct folio *folio, unsigned int new_order, struct page *page,
|
||||
struct list_head *list);
|
||||
/*
|
||||
* try_folio_split - try to split a @folio at @page using non uniform split.
|
||||
* try_folio_split_to_order - try to split a @folio at @page to @new_order using
|
||||
* non uniform split.
|
||||
* @folio: folio to be split
|
||||
* @page: split to order-0 at the given page
|
||||
* @list: store the after-split folios
|
||||
* @page: split to @new_order at the given page
|
||||
* @new_order: the target split order
|
||||
*
|
||||
* Try to split a @folio at @page using non uniform split to order-0, if
|
||||
* non uniform split is not supported, fall back to uniform split.
|
||||
* Try to split a @folio at @page using non uniform split to @new_order, if
|
||||
* non uniform split is not supported, fall back to uniform split. After-split
|
||||
* folios are put back to LRU list. Use min_order_for_split() to get the lower
|
||||
* bound of @new_order.
|
||||
*
|
||||
* Return: 0: split is successful, otherwise split failed.
|
||||
*/
|
||||
static inline int try_folio_split(struct folio *folio, struct page *page,
|
||||
struct list_head *list)
|
||||
static inline int try_folio_split_to_order(struct folio *folio,
|
||||
struct page *page, unsigned int new_order)
|
||||
{
|
||||
int ret = min_order_for_split(folio);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (!non_uniform_split_supported(folio, 0, false))
|
||||
return split_huge_page_to_list_to_order(&folio->page, list,
|
||||
ret);
|
||||
return folio_split(folio, ret, page, list);
|
||||
if (!non_uniform_split_supported(folio, new_order, /* warns= */ false))
|
||||
return split_huge_page_to_list_to_order(&folio->page, NULL,
|
||||
new_order);
|
||||
return folio_split(folio, new_order, page, NULL);
|
||||
}
|
||||
static inline int split_huge_page(struct page *page)
|
||||
{
|
||||
struct folio *folio = page_folio(page);
|
||||
int ret = min_order_for_split(folio);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* split_huge_page() locks the page before splitting and
|
||||
* expects the same page that has been split to be locked when
|
||||
* returned. split_folio(page_folio(page)) cannot be used here
|
||||
* because it converts the page to folio and passes the head
|
||||
* page to be split.
|
||||
*/
|
||||
return split_huge_page_to_list_to_order(page, NULL, ret);
|
||||
return split_huge_page_to_list_to_order(page, NULL, 0);
|
||||
}
|
||||
void deferred_split_folio(struct folio *folio, bool partially_mapped);
|
||||
|
||||
@@ -597,14 +582,20 @@ static inline int split_huge_page(struct page *page)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static inline int min_order_for_split(struct folio *folio)
|
||||
{
|
||||
VM_WARN_ON_ONCE_FOLIO(1, folio);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static inline int split_folio_to_list(struct folio *folio, struct list_head *list)
|
||||
{
|
||||
VM_WARN_ON_ONCE_FOLIO(1, folio);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static inline int try_folio_split(struct folio *folio, struct page *page,
|
||||
struct list_head *list)
|
||||
static inline int try_folio_split_to_order(struct folio *folio,
|
||||
struct page *page, unsigned int new_order)
|
||||
{
|
||||
VM_WARN_ON_ONCE_FOLIO(1, folio);
|
||||
return -EINVAL;
|
||||
|
||||
+10
-3
@@ -2074,7 +2074,7 @@ static inline unsigned long folio_nr_pages(const struct folio *folio)
|
||||
return folio_large_nr_pages(folio);
|
||||
}
|
||||
|
||||
#if !defined(CONFIG_ARCH_HAS_GIGANTIC_PAGE)
|
||||
#if !defined(CONFIG_HAVE_GIGANTIC_FOLIOS)
|
||||
/*
|
||||
* We don't expect any folios that exceed buddy sizes (and consequently
|
||||
* memory sections).
|
||||
@@ -2087,10 +2087,17 @@ static inline unsigned long folio_nr_pages(const struct folio *folio)
|
||||
* pages are guaranteed to be contiguous.
|
||||
*/
|
||||
#define MAX_FOLIO_ORDER PFN_SECTION_SHIFT
|
||||
#else
|
||||
#elif defined(CONFIG_HUGETLB_PAGE)
|
||||
/*
|
||||
* There is no real limit on the folio size. We limit them to the maximum we
|
||||
* currently expect (e.g., hugetlb, dax).
|
||||
* currently expect (see CONFIG_HAVE_GIGANTIC_FOLIOS): with hugetlb, we expect
|
||||
* no folios larger than 16 GiB on 64bit and 1 GiB on 32bit.
|
||||
*/
|
||||
#define MAX_FOLIO_ORDER get_order(IS_ENABLED(CONFIG_64BIT) ? SZ_16G : SZ_1G)
|
||||
#else
|
||||
/*
|
||||
* Without hugetlb, gigantic folios that are bigger than a single PUD are
|
||||
* currently impossible.
|
||||
*/
|
||||
#define MAX_FOLIO_ORDER PUD_ORDER
|
||||
#endif
|
||||
|
||||
@@ -109,6 +109,15 @@ config KEXEC_HANDOVER
|
||||
to keep data or state alive across the kexec. For this to work,
|
||||
both source and target kernels need to have this option enabled.
|
||||
|
||||
config KEXEC_HANDOVER_DEBUG
|
||||
bool "Enable Kexec Handover debug checks"
|
||||
depends on KEXEC_HANDOVER
|
||||
help
|
||||
This option enables extra sanity checks for the Kexec Handover
|
||||
subsystem. Since, KHO performance is crucial in live update
|
||||
scenarios and the extra code might be adding overhead it is
|
||||
only optionally enabled.
|
||||
|
||||
config CRASH_DUMP
|
||||
bool "kernel crash dumps"
|
||||
default ARCH_DEFAULT_CRASH_DUMP
|
||||
|
||||
@@ -83,6 +83,7 @@ obj-$(CONFIG_KEXEC) += kexec.o
|
||||
obj-$(CONFIG_KEXEC_FILE) += kexec_file.o
|
||||
obj-$(CONFIG_KEXEC_ELF) += kexec_elf.o
|
||||
obj-$(CONFIG_KEXEC_HANDOVER) += kexec_handover.o
|
||||
obj-$(CONFIG_KEXEC_HANDOVER_DEBUG) += kexec_handover_debug.o
|
||||
obj-$(CONFIG_BACKTRACE_SELF_TEST) += backtracetest.o
|
||||
obj-$(CONFIG_COMPAT) += compat.o
|
||||
obj-$(CONFIG_CGROUPS) += cgroup/
|
||||
|
||||
+1
-1
@@ -373,7 +373,7 @@ static int __crash_shrink_memory(struct resource *old_res,
|
||||
old_res->start = 0;
|
||||
old_res->end = 0;
|
||||
} else {
|
||||
crashk_res.end = ram_res->start - 1;
|
||||
old_res->end = ram_res->start - 1;
|
||||
}
|
||||
|
||||
crash_free_reserved_phys_range(ram_res->start, ram_res->end);
|
||||
|
||||
@@ -18,7 +18,9 @@
|
||||
#include <linux/mm.h>
|
||||
#include "gcov.h"
|
||||
|
||||
#if (__GNUC__ >= 14)
|
||||
#if (__GNUC__ >= 15)
|
||||
#define GCOV_COUNTERS 10
|
||||
#elif (__GNUC__ >= 14)
|
||||
#define GCOV_COUNTERS 9
|
||||
#elif (__GNUC__ >= 10)
|
||||
#define GCOV_COUNTERS 8
|
||||
|
||||
+58
-37
@@ -8,6 +8,7 @@
|
||||
|
||||
#define pr_fmt(fmt) "KHO: " fmt
|
||||
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/cma.h>
|
||||
#include <linux/count_zeros.h>
|
||||
#include <linux/debugfs.h>
|
||||
@@ -22,6 +23,7 @@
|
||||
|
||||
#include <asm/early_ioremap.h>
|
||||
|
||||
#include "kexec_handover_internal.h"
|
||||
/*
|
||||
* KHO is tightly coupled with mm init and needs access to some of mm
|
||||
* internal APIs.
|
||||
@@ -67,10 +69,10 @@ early_param("kho", kho_parse_enable);
|
||||
* Keep track of memory that is to be preserved across KHO.
|
||||
*
|
||||
* The serializing side uses two levels of xarrays to manage chunks of per-order
|
||||
* 512 byte bitmaps. For instance if PAGE_SIZE = 4096, the entire 1G order of a
|
||||
* 1TB system would fit inside a single 512 byte bitmap. For order 0 allocations
|
||||
* each bitmap will cover 16M of address space. Thus, for 16G of memory at most
|
||||
* 512K of bitmap memory will be needed for order 0.
|
||||
* PAGE_SIZE byte bitmaps. For instance if PAGE_SIZE = 4096, the entire 1G order
|
||||
* of a 8TB system would fit inside a single 4096 byte bitmap. For order 0
|
||||
* allocations each bitmap will cover 128M of address space. Thus, for 16G of
|
||||
* memory at most 512K of bitmap memory will be needed for order 0.
|
||||
*
|
||||
* This approach is fully incremental, as the serialization progresses folios
|
||||
* can continue be aggregated to the tracker. The final step, immediately prior
|
||||
@@ -78,12 +80,14 @@ early_param("kho", kho_parse_enable);
|
||||
* successor kernel to parse.
|
||||
*/
|
||||
|
||||
#define PRESERVE_BITS (512 * 8)
|
||||
#define PRESERVE_BITS (PAGE_SIZE * 8)
|
||||
|
||||
struct kho_mem_phys_bits {
|
||||
DECLARE_BITMAP(preserve, PRESERVE_BITS);
|
||||
};
|
||||
|
||||
static_assert(sizeof(struct kho_mem_phys_bits) == PAGE_SIZE);
|
||||
|
||||
struct kho_mem_phys {
|
||||
/*
|
||||
* Points to kho_mem_phys_bits, a sparse bitmap array. Each bit is sized
|
||||
@@ -131,28 +135,28 @@ static struct kho_out kho_out = {
|
||||
.finalized = false,
|
||||
};
|
||||
|
||||
static void *xa_load_or_alloc(struct xarray *xa, unsigned long index, size_t sz)
|
||||
static void *xa_load_or_alloc(struct xarray *xa, unsigned long index)
|
||||
{
|
||||
void *elm, *res;
|
||||
void *res = xa_load(xa, index);
|
||||
|
||||
elm = xa_load(xa, index);
|
||||
if (elm)
|
||||
return elm;
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
void *elm __free(free_page) = (void *)get_zeroed_page(GFP_KERNEL);
|
||||
|
||||
elm = kzalloc(sz, GFP_KERNEL);
|
||||
if (!elm)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
if (WARN_ON(kho_scratch_overlap(virt_to_phys(elm), PAGE_SIZE)))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
res = xa_cmpxchg(xa, index, NULL, elm, GFP_KERNEL);
|
||||
if (xa_is_err(res))
|
||||
res = ERR_PTR(xa_err(res));
|
||||
|
||||
if (res) {
|
||||
kfree(elm);
|
||||
return ERR_PTR(xa_err(res));
|
||||
else if (res)
|
||||
return res;
|
||||
}
|
||||
|
||||
return elm;
|
||||
return no_free_ptr(elm);
|
||||
}
|
||||
|
||||
static void __kho_unpreserve(struct kho_mem_track *track, unsigned long pfn,
|
||||
@@ -167,12 +171,12 @@ static void __kho_unpreserve(struct kho_mem_track *track, unsigned long pfn,
|
||||
const unsigned long pfn_high = pfn >> order;
|
||||
|
||||
physxa = xa_load(&track->orders, order);
|
||||
if (!physxa)
|
||||
continue;
|
||||
if (WARN_ON_ONCE(!physxa))
|
||||
return;
|
||||
|
||||
bits = xa_load(&physxa->phys_bits, pfn_high / PRESERVE_BITS);
|
||||
if (!bits)
|
||||
continue;
|
||||
if (WARN_ON_ONCE(!bits))
|
||||
return;
|
||||
|
||||
clear_bit(pfn_high % PRESERVE_BITS, bits->preserve);
|
||||
|
||||
@@ -216,8 +220,7 @@ static int __kho_preserve_order(struct kho_mem_track *track, unsigned long pfn,
|
||||
}
|
||||
}
|
||||
|
||||
bits = xa_load_or_alloc(&physxa->phys_bits, pfn_high / PRESERVE_BITS,
|
||||
sizeof(*bits));
|
||||
bits = xa_load_or_alloc(&physxa->phys_bits, pfn_high / PRESERVE_BITS);
|
||||
if (IS_ERR(bits))
|
||||
return PTR_ERR(bits);
|
||||
|
||||
@@ -345,15 +348,19 @@ static_assert(sizeof(struct khoser_mem_chunk) == PAGE_SIZE);
|
||||
static struct khoser_mem_chunk *new_chunk(struct khoser_mem_chunk *cur_chunk,
|
||||
unsigned long order)
|
||||
{
|
||||
struct khoser_mem_chunk *chunk;
|
||||
struct khoser_mem_chunk *chunk __free(free_page) = NULL;
|
||||
|
||||
chunk = kzalloc(PAGE_SIZE, GFP_KERNEL);
|
||||
chunk = (void *)get_zeroed_page(GFP_KERNEL);
|
||||
if (!chunk)
|
||||
return NULL;
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
if (WARN_ON(kho_scratch_overlap(virt_to_phys(chunk), PAGE_SIZE)))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
chunk->hdr.order = order;
|
||||
if (cur_chunk)
|
||||
KHOSER_STORE_PTR(cur_chunk->hdr.next, chunk);
|
||||
return chunk;
|
||||
return no_free_ptr(chunk);
|
||||
}
|
||||
|
||||
static void kho_mem_ser_free(struct khoser_mem_chunk *first_chunk)
|
||||
@@ -374,14 +381,17 @@ static int kho_mem_serialize(struct kho_serialization *ser)
|
||||
struct khoser_mem_chunk *chunk = NULL;
|
||||
struct kho_mem_phys *physxa;
|
||||
unsigned long order;
|
||||
int err = -ENOMEM;
|
||||
|
||||
xa_for_each(&ser->track.orders, order, physxa) {
|
||||
struct kho_mem_phys_bits *bits;
|
||||
unsigned long phys;
|
||||
|
||||
chunk = new_chunk(chunk, order);
|
||||
if (!chunk)
|
||||
if (IS_ERR(chunk)) {
|
||||
err = PTR_ERR(chunk);
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
if (!first_chunk)
|
||||
first_chunk = chunk;
|
||||
@@ -391,8 +401,10 @@ static int kho_mem_serialize(struct kho_serialization *ser)
|
||||
|
||||
if (chunk->hdr.num_elms == ARRAY_SIZE(chunk->bitmaps)) {
|
||||
chunk = new_chunk(chunk, order);
|
||||
if (!chunk)
|
||||
if (IS_ERR(chunk)) {
|
||||
err = PTR_ERR(chunk);
|
||||
goto err_free;
|
||||
}
|
||||
}
|
||||
|
||||
elm = &chunk->bitmaps[chunk->hdr.num_elms];
|
||||
@@ -409,7 +421,7 @@ static int kho_mem_serialize(struct kho_serialization *ser)
|
||||
|
||||
err_free:
|
||||
kho_mem_ser_free(first_chunk);
|
||||
return -ENOMEM;
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __init deserialize_bitmap(unsigned int order,
|
||||
@@ -465,8 +477,8 @@ static void __init kho_mem_deserialize(const void *fdt)
|
||||
* area for early allocations that happen before page allocator is
|
||||
* initialized.
|
||||
*/
|
||||
static struct kho_scratch *kho_scratch;
|
||||
static unsigned int kho_scratch_cnt;
|
||||
struct kho_scratch *kho_scratch;
|
||||
unsigned int kho_scratch_cnt;
|
||||
|
||||
/*
|
||||
* The scratch areas are scaled by default as percent of memory allocated from
|
||||
@@ -752,6 +764,9 @@ int kho_preserve_folio(struct folio *folio)
|
||||
const unsigned int order = folio_order(folio);
|
||||
struct kho_mem_track *track = &kho_out.ser.track;
|
||||
|
||||
if (WARN_ON(kho_scratch_overlap(pfn << PAGE_SHIFT, PAGE_SIZE << order)))
|
||||
return -EINVAL;
|
||||
|
||||
return __kho_preserve_order(track, pfn, order);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kho_preserve_folio);
|
||||
@@ -775,6 +790,11 @@ int kho_preserve_pages(struct page *page, unsigned int nr_pages)
|
||||
unsigned long failed_pfn = 0;
|
||||
int err = 0;
|
||||
|
||||
if (WARN_ON(kho_scratch_overlap(start_pfn << PAGE_SHIFT,
|
||||
nr_pages << PAGE_SHIFT))) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
while (pfn < end_pfn) {
|
||||
const unsigned int order =
|
||||
min(count_trailing_zeros(pfn), ilog2(end_pfn - pfn));
|
||||
@@ -862,16 +882,17 @@ err_free:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void kho_vmalloc_unpreserve_chunk(struct kho_vmalloc_chunk *chunk)
|
||||
static void kho_vmalloc_unpreserve_chunk(struct kho_vmalloc_chunk *chunk,
|
||||
unsigned short order)
|
||||
{
|
||||
struct kho_mem_track *track = &kho_out.ser.track;
|
||||
unsigned long pfn = PHYS_PFN(virt_to_phys(chunk));
|
||||
|
||||
__kho_unpreserve(track, pfn, pfn + 1);
|
||||
|
||||
for (int i = 0; chunk->phys[i]; i++) {
|
||||
for (int i = 0; i < ARRAY_SIZE(chunk->phys) && chunk->phys[i]; i++) {
|
||||
pfn = PHYS_PFN(chunk->phys[i]);
|
||||
__kho_unpreserve(track, pfn, pfn + 1);
|
||||
__kho_unpreserve(track, pfn, pfn + (1 << order));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -882,7 +903,7 @@ static void kho_vmalloc_free_chunks(struct kho_vmalloc *kho_vmalloc)
|
||||
while (chunk) {
|
||||
struct kho_vmalloc_chunk *tmp = chunk;
|
||||
|
||||
kho_vmalloc_unpreserve_chunk(chunk);
|
||||
kho_vmalloc_unpreserve_chunk(chunk, kho_vmalloc->order);
|
||||
|
||||
chunk = KHOSER_LOAD_PTR(chunk->hdr.next);
|
||||
free_page((unsigned long)tmp);
|
||||
@@ -992,7 +1013,7 @@ void *kho_restore_vmalloc(const struct kho_vmalloc *preservation)
|
||||
while (chunk) {
|
||||
struct page *page;
|
||||
|
||||
for (int i = 0; chunk->phys[i]; i++) {
|
||||
for (int i = 0; i < ARRAY_SIZE(chunk->phys) && chunk->phys[i]; i++) {
|
||||
phys_addr_t phys = chunk->phys[i];
|
||||
|
||||
if (idx + contig_pages > total_pages)
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* kexec_handover_debug.c - kexec handover optional debug functionality
|
||||
* Copyright (C) 2025 Google LLC, Pasha Tatashin <pasha.tatashin@soleen.com>
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "KHO: " fmt
|
||||
|
||||
#include "kexec_handover_internal.h"
|
||||
|
||||
bool kho_scratch_overlap(phys_addr_t phys, size_t size)
|
||||
{
|
||||
phys_addr_t scratch_start, scratch_end;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < kho_scratch_cnt; i++) {
|
||||
scratch_start = kho_scratch[i].addr;
|
||||
scratch_end = kho_scratch[i].addr + kho_scratch[i].size;
|
||||
|
||||
if (phys < scratch_end && (phys + size) > scratch_start)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef LINUX_KEXEC_HANDOVER_INTERNAL_H
|
||||
#define LINUX_KEXEC_HANDOVER_INTERNAL_H
|
||||
|
||||
#include <linux/kexec_handover.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
extern struct kho_scratch *kho_scratch;
|
||||
extern unsigned int kho_scratch_cnt;
|
||||
|
||||
#ifdef CONFIG_KEXEC_HANDOVER_DEBUG
|
||||
bool kho_scratch_overlap(phys_addr_t phys, size_t size);
|
||||
#else
|
||||
static inline bool kho_scratch_overlap(phys_addr_t phys, size_t size)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif /* CONFIG_KEXEC_HANDOVER_DEBUG */
|
||||
|
||||
#endif /* LINUX_KEXEC_HANDOVER_INTERNAL_H */
|
||||
+16
-14
@@ -64,6 +64,8 @@
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include <trace/events/maple_tree.h>
|
||||
|
||||
#define TP_FCT tracepoint_string(__func__)
|
||||
|
||||
/*
|
||||
* Kernel pointer hashing renders much of the maple tree dump useless as tagged
|
||||
* pointers get hashed to arbitrary values.
|
||||
@@ -2756,7 +2758,7 @@ static inline void mas_rebalance(struct ma_state *mas,
|
||||
MA_STATE(l_mas, mas->tree, mas->index, mas->last);
|
||||
MA_STATE(r_mas, mas->tree, mas->index, mas->last);
|
||||
|
||||
trace_ma_op(__func__, mas);
|
||||
trace_ma_op(TP_FCT, mas);
|
||||
|
||||
/*
|
||||
* Rebalancing occurs if a node is insufficient. Data is rebalanced
|
||||
@@ -2997,7 +2999,7 @@ static void mas_split(struct ma_state *mas, struct maple_big_node *b_node)
|
||||
MA_STATE(prev_l_mas, mas->tree, mas->index, mas->last);
|
||||
MA_STATE(prev_r_mas, mas->tree, mas->index, mas->last);
|
||||
|
||||
trace_ma_op(__func__, mas);
|
||||
trace_ma_op(TP_FCT, mas);
|
||||
|
||||
mast.l = &l_mas;
|
||||
mast.r = &r_mas;
|
||||
@@ -3172,7 +3174,7 @@ static bool mas_is_span_wr(struct ma_wr_state *wr_mas)
|
||||
return false;
|
||||
}
|
||||
|
||||
trace_ma_write(__func__, wr_mas->mas, wr_mas->r_max, entry);
|
||||
trace_ma_write(TP_FCT, wr_mas->mas, wr_mas->r_max, entry);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -3416,7 +3418,7 @@ static noinline void mas_wr_spanning_store(struct ma_wr_state *wr_mas)
|
||||
* of data may happen.
|
||||
*/
|
||||
mas = wr_mas->mas;
|
||||
trace_ma_op(__func__, mas);
|
||||
trace_ma_op(TP_FCT, mas);
|
||||
|
||||
if (unlikely(!mas->index && mas->last == ULONG_MAX))
|
||||
return mas_new_root(mas, wr_mas->entry);
|
||||
@@ -3552,7 +3554,7 @@ done:
|
||||
} else {
|
||||
memcpy(wr_mas->node, newnode, sizeof(struct maple_node));
|
||||
}
|
||||
trace_ma_write(__func__, mas, 0, wr_mas->entry);
|
||||
trace_ma_write(TP_FCT, mas, 0, wr_mas->entry);
|
||||
mas_update_gap(mas);
|
||||
mas->end = new_end;
|
||||
return;
|
||||
@@ -3596,7 +3598,7 @@ static inline void mas_wr_slot_store(struct ma_wr_state *wr_mas)
|
||||
mas->offset++; /* Keep mas accurate. */
|
||||
}
|
||||
|
||||
trace_ma_write(__func__, mas, 0, wr_mas->entry);
|
||||
trace_ma_write(TP_FCT, mas, 0, wr_mas->entry);
|
||||
/*
|
||||
* Only update gap when the new entry is empty or there is an empty
|
||||
* entry in the original two ranges.
|
||||
@@ -3717,7 +3719,7 @@ static inline void mas_wr_append(struct ma_wr_state *wr_mas,
|
||||
mas_update_gap(mas);
|
||||
|
||||
mas->end = new_end;
|
||||
trace_ma_write(__func__, mas, new_end, wr_mas->entry);
|
||||
trace_ma_write(TP_FCT, mas, new_end, wr_mas->entry);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -3731,7 +3733,7 @@ static void mas_wr_bnode(struct ma_wr_state *wr_mas)
|
||||
{
|
||||
struct maple_big_node b_node;
|
||||
|
||||
trace_ma_write(__func__, wr_mas->mas, 0, wr_mas->entry);
|
||||
trace_ma_write(TP_FCT, wr_mas->mas, 0, wr_mas->entry);
|
||||
memset(&b_node, 0, sizeof(struct maple_big_node));
|
||||
mas_store_b_node(wr_mas, &b_node, wr_mas->offset_end);
|
||||
mas_commit_b_node(wr_mas, &b_node);
|
||||
@@ -5062,7 +5064,7 @@ void *mas_store(struct ma_state *mas, void *entry)
|
||||
{
|
||||
MA_WR_STATE(wr_mas, mas, entry);
|
||||
|
||||
trace_ma_write(__func__, mas, 0, entry);
|
||||
trace_ma_write(TP_FCT, mas, 0, entry);
|
||||
#ifdef CONFIG_DEBUG_MAPLE_TREE
|
||||
if (MAS_WARN_ON(mas, mas->index > mas->last))
|
||||
pr_err("Error %lX > %lX " PTR_FMT "\n", mas->index, mas->last,
|
||||
@@ -5163,7 +5165,7 @@ void mas_store_prealloc(struct ma_state *mas, void *entry)
|
||||
}
|
||||
|
||||
store:
|
||||
trace_ma_write(__func__, mas, 0, entry);
|
||||
trace_ma_write(TP_FCT, mas, 0, entry);
|
||||
mas_wr_store_entry(&wr_mas);
|
||||
MAS_WR_BUG_ON(&wr_mas, mas_is_err(mas));
|
||||
mas_destroy(mas);
|
||||
@@ -5882,7 +5884,7 @@ void *mtree_load(struct maple_tree *mt, unsigned long index)
|
||||
MA_STATE(mas, mt, index, index);
|
||||
void *entry;
|
||||
|
||||
trace_ma_read(__func__, &mas);
|
||||
trace_ma_read(TP_FCT, &mas);
|
||||
rcu_read_lock();
|
||||
retry:
|
||||
entry = mas_start(&mas);
|
||||
@@ -5925,7 +5927,7 @@ int mtree_store_range(struct maple_tree *mt, unsigned long index,
|
||||
MA_STATE(mas, mt, index, last);
|
||||
int ret = 0;
|
||||
|
||||
trace_ma_write(__func__, &mas, 0, entry);
|
||||
trace_ma_write(TP_FCT, &mas, 0, entry);
|
||||
if (WARN_ON_ONCE(xa_is_advanced(entry)))
|
||||
return -EINVAL;
|
||||
|
||||
@@ -6148,7 +6150,7 @@ void *mtree_erase(struct maple_tree *mt, unsigned long index)
|
||||
void *entry = NULL;
|
||||
|
||||
MA_STATE(mas, mt, index, index);
|
||||
trace_ma_op(__func__, &mas);
|
||||
trace_ma_op(TP_FCT, &mas);
|
||||
|
||||
mtree_lock(mt);
|
||||
entry = mas_erase(&mas);
|
||||
@@ -6485,7 +6487,7 @@ void *mt_find(struct maple_tree *mt, unsigned long *index, unsigned long max)
|
||||
unsigned long copy = *index;
|
||||
#endif
|
||||
|
||||
trace_ma_read(__func__, &mas);
|
||||
trace_ma_read(TP_FCT, &mas);
|
||||
|
||||
if ((*index) > max)
|
||||
return NULL;
|
||||
|
||||
@@ -301,6 +301,9 @@ static int __init kho_test_init(void)
|
||||
phys_addr_t fdt_phys;
|
||||
int err;
|
||||
|
||||
if (!kho_is_enabled())
|
||||
return 0;
|
||||
|
||||
err = kho_retrieve_subtree(KHO_TEST_FDT, &fdt_phys);
|
||||
if (!err)
|
||||
return kho_test_restore(fdt_phys);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user