mirror of
https://github.com/torvalds/linux.git
synced 2026-04-18 14:53:58 -04:00
liveupdate: kho: move to kernel/liveupdate
Move KHO to kernel/liveupdate/ in preparation of placing all Live Update core kernel related files to the same place. [pasha.tatashin@soleen.com: disable the menu when DEFERRED_STRUCT_PAGE_INIT] Link: https://lkml.kernel.org/r/CA+CK2bAvh9Oa2SLfsbJ8zztpEjrgr_hr-uGgF1coy8yoibT39A@mail.gmail.com Link: https://lkml.kernel.org/r/20251101142325.1326536-8-pasha.tatashin@soleen.com Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com> Reviewed-by: Jason Gunthorpe <jgg@nvidia.com> Reviewed-by: Mike Rapoport (Microsoft) <rppt@kernel.org> Cc: Alexander Graf <graf@amazon.com> Cc: Changyuan Lyu <changyuanl@google.com> Cc: Christian Brauner <brauner@kernel.org> Cc: Jason Gunthorpe <jgg@ziepe.ca> Cc: Jonathan Corbet <corbet@lwn.net> Cc: Masahiro Yamada <masahiroy@kernel.org> Cc: Miguel Ojeda <ojeda@kernel.org> Cc: Pratyush Yadav <pratyush@kernel.org> Cc: Randy Dunlap <rdunlap@infradead.org> Cc: Simon Horman <horms@kernel.org> Cc: Tejun Heo <tj@kernel.org> Cc: Zhu Yanjun <yanjun.zhu@linux.dev> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
This commit is contained in:
committed by
Andrew Morton
parent
99cd2ffac6
commit
48a1b2321d
219
kernel/liveupdate/kexec_handover_debugfs.c
Normal file
219
kernel/liveupdate/kexec_handover_debugfs.c
Normal file
@@ -0,0 +1,219 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* kexec_handover_debugfs.c - kexec handover debugfs interfaces
|
||||
* Copyright (C) 2023 Alexander Graf <graf@amazon.com>
|
||||
* Copyright (C) 2025 Microsoft Corporation, Mike Rapoport <rppt@kernel.org>
|
||||
* Copyright (C) 2025 Google LLC, Changyuan Lyu <changyuanl@google.com>
|
||||
* Copyright (C) 2025 Google LLC, Pasha Tatashin <pasha.tatashin@soleen.com>
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "KHO: " fmt
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/libfdt.h>
|
||||
#include <linux/mm.h>
|
||||
#include "kexec_handover_internal.h"
|
||||
|
||||
static struct dentry *debugfs_root;
|
||||
|
||||
struct fdt_debugfs {
|
||||
struct list_head list;
|
||||
struct debugfs_blob_wrapper wrapper;
|
||||
struct dentry *file;
|
||||
};
|
||||
|
||||
static int __kho_debugfs_fdt_add(struct list_head *list, struct dentry *dir,
|
||||
const char *name, const void *fdt)
|
||||
{
|
||||
struct fdt_debugfs *f;
|
||||
struct dentry *file;
|
||||
|
||||
f = kmalloc(sizeof(*f), GFP_KERNEL);
|
||||
if (!f)
|
||||
return -ENOMEM;
|
||||
|
||||
f->wrapper.data = (void *)fdt;
|
||||
f->wrapper.size = fdt_totalsize(fdt);
|
||||
|
||||
file = debugfs_create_blob(name, 0400, dir, &f->wrapper);
|
||||
if (IS_ERR(file)) {
|
||||
kfree(f);
|
||||
return PTR_ERR(file);
|
||||
}
|
||||
|
||||
f->file = file;
|
||||
list_add(&f->list, list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int kho_debugfs_fdt_add(struct kho_debugfs *dbg, const char *name,
|
||||
const void *fdt, bool root)
|
||||
{
|
||||
struct dentry *dir;
|
||||
|
||||
if (root)
|
||||
dir = dbg->dir;
|
||||
else
|
||||
dir = dbg->sub_fdt_dir;
|
||||
|
||||
return __kho_debugfs_fdt_add(&dbg->fdt_list, dir, name, fdt);
|
||||
}
|
||||
|
||||
void kho_debugfs_fdt_remove(struct kho_debugfs *dbg, void *fdt)
|
||||
{
|
||||
struct fdt_debugfs *ff;
|
||||
|
||||
list_for_each_entry(ff, &dbg->fdt_list, list) {
|
||||
if (ff->wrapper.data == fdt) {
|
||||
debugfs_remove(ff->file);
|
||||
list_del(&ff->list);
|
||||
kfree(ff);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int kho_out_finalize_get(void *data, u64 *val)
|
||||
{
|
||||
*val = kho_finalized();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kho_out_finalize_set(void *data, u64 val)
|
||||
{
|
||||
if (val)
|
||||
return kho_finalize();
|
||||
else
|
||||
return kho_abort();
|
||||
}
|
||||
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(kho_out_finalize_fops, kho_out_finalize_get,
|
||||
kho_out_finalize_set, "%llu\n");
|
||||
|
||||
static int scratch_phys_show(struct seq_file *m, void *v)
|
||||
{
|
||||
for (int i = 0; i < kho_scratch_cnt; i++)
|
||||
seq_printf(m, "0x%llx\n", kho_scratch[i].addr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
DEFINE_SHOW_ATTRIBUTE(scratch_phys);
|
||||
|
||||
static int scratch_len_show(struct seq_file *m, void *v)
|
||||
{
|
||||
for (int i = 0; i < kho_scratch_cnt; i++)
|
||||
seq_printf(m, "0x%llx\n", kho_scratch[i].size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
DEFINE_SHOW_ATTRIBUTE(scratch_len);
|
||||
|
||||
__init void kho_in_debugfs_init(struct kho_debugfs *dbg, const void *fdt)
|
||||
{
|
||||
struct dentry *dir, *sub_fdt_dir;
|
||||
int err, child;
|
||||
|
||||
INIT_LIST_HEAD(&dbg->fdt_list);
|
||||
|
||||
dir = debugfs_create_dir("in", debugfs_root);
|
||||
if (IS_ERR(dir)) {
|
||||
err = PTR_ERR(dir);
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
sub_fdt_dir = debugfs_create_dir("sub_fdts", dir);
|
||||
if (IS_ERR(sub_fdt_dir)) {
|
||||
err = PTR_ERR(sub_fdt_dir);
|
||||
goto err_rmdir;
|
||||
}
|
||||
|
||||
err = __kho_debugfs_fdt_add(&dbg->fdt_list, dir, "fdt", fdt);
|
||||
if (err)
|
||||
goto err_rmdir;
|
||||
|
||||
fdt_for_each_subnode(child, fdt, 0) {
|
||||
int len = 0;
|
||||
const char *name = fdt_get_name(fdt, child, NULL);
|
||||
const u64 *fdt_phys;
|
||||
|
||||
fdt_phys = fdt_getprop(fdt, child, "fdt", &len);
|
||||
if (!fdt_phys)
|
||||
continue;
|
||||
if (len != sizeof(*fdt_phys)) {
|
||||
pr_warn("node %s prop fdt has invalid length: %d\n",
|
||||
name, len);
|
||||
continue;
|
||||
}
|
||||
err = __kho_debugfs_fdt_add(&dbg->fdt_list, sub_fdt_dir, name,
|
||||
phys_to_virt(*fdt_phys));
|
||||
if (err) {
|
||||
pr_warn("failed to add fdt %s to debugfs: %d\n", name,
|
||||
err);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
dbg->dir = dir;
|
||||
dbg->sub_fdt_dir = sub_fdt_dir;
|
||||
|
||||
return;
|
||||
err_rmdir:
|
||||
debugfs_remove_recursive(dir);
|
||||
err_out:
|
||||
/*
|
||||
* Failure to create /sys/kernel/debug/kho/in does not prevent
|
||||
* reviving state from KHO and setting up KHO for the next
|
||||
* kexec.
|
||||
*/
|
||||
if (err)
|
||||
pr_err("failed exposing handover FDT in debugfs: %d\n", err);
|
||||
}
|
||||
|
||||
__init int kho_out_debugfs_init(struct kho_debugfs *dbg)
|
||||
{
|
||||
struct dentry *dir, *f, *sub_fdt_dir;
|
||||
|
||||
INIT_LIST_HEAD(&dbg->fdt_list);
|
||||
|
||||
dir = debugfs_create_dir("out", debugfs_root);
|
||||
if (IS_ERR(dir))
|
||||
return -ENOMEM;
|
||||
|
||||
sub_fdt_dir = debugfs_create_dir("sub_fdts", dir);
|
||||
if (IS_ERR(sub_fdt_dir))
|
||||
goto err_rmdir;
|
||||
|
||||
f = debugfs_create_file("scratch_phys", 0400, dir, NULL,
|
||||
&scratch_phys_fops);
|
||||
if (IS_ERR(f))
|
||||
goto err_rmdir;
|
||||
|
||||
f = debugfs_create_file("scratch_len", 0400, dir, NULL,
|
||||
&scratch_len_fops);
|
||||
if (IS_ERR(f))
|
||||
goto err_rmdir;
|
||||
|
||||
f = debugfs_create_file("finalize", 0600, dir, NULL,
|
||||
&kho_out_finalize_fops);
|
||||
if (IS_ERR(f))
|
||||
goto err_rmdir;
|
||||
|
||||
dbg->dir = dir;
|
||||
dbg->sub_fdt_dir = sub_fdt_dir;
|
||||
return 0;
|
||||
|
||||
err_rmdir:
|
||||
debugfs_remove_recursive(dir);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
__init int kho_debugfs_init(void)
|
||||
{
|
||||
debugfs_root = debugfs_create_dir("kho", NULL);
|
||||
if (IS_ERR(debugfs_root))
|
||||
return -ENOENT;
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user