This is simple test and playground useful kstate subsystem development. It contains some structure with different kind of data which migrated across kexec to the new kernel using kstate. Signed-off-by: Andrey Ryabinin <arbn@xxxxxxxxxxxxxxx> --- include/linux/kstate.h | 3 ++ kernel/kstate.c | 5 +++ lib/Makefile | 2 + lib/test_kstate.c | 86 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 96 insertions(+) create mode 100644 lib/test_kstate.c diff --git a/include/linux/kstate.h b/include/linux/kstate.h index 36cfefd87572..0bde76aa4d8f 100644 --- a/include/linux/kstate.h +++ b/include/linux/kstate.h @@ -90,6 +90,7 @@ struct kstate_field { enum kstate_ids { KSTATE_RSVD_MEM_ID = 1, KSTATE_STRUCT_PAGE_ID, + KSTATE_TEST_ID, KSTATE_LAST_ID = -1, }; @@ -132,6 +133,8 @@ extern struct kstate_description page_state; void kstate_init(void); +bool is_kstate_kernel(void); + int kstate_save_state(void); void free_kstate_stream(void); diff --git a/kernel/kstate.c b/kernel/kstate.c index 68a1272abceb..3d9b786da72a 100644 --- a/kernel/kstate.c +++ b/kernel/kstate.c @@ -287,6 +287,11 @@ static void restore_migrate_state(unsigned long kstate_data, static unsigned long kstate_stream_addr = -1; static unsigned long kstate_size; +bool is_kstate_kernel(void) +{ + return kstate_stream_addr != -1; +} + static void __kstate_register(struct kstate_description *state, void *obj, struct state_entry *se) { diff --git a/lib/Makefile b/lib/Makefile index d5cfc7afbbb8..1395b852b58d 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -356,6 +356,8 @@ obj-$(CONFIG_PARMAN) += parman.o obj-y += group_cpus.o +obj-$(CONFIG_KSTATE) += test_kstate.o + # GCC library routines obj-$(CONFIG_GENERIC_LIB_ASHLDI3) += ashldi3.o obj-$(CONFIG_GENERIC_LIB_ASHRDI3) += ashrdi3.o diff --git a/lib/test_kstate.c b/lib/test_kstate.c new file mode 100644 index 000000000000..1d9feb017415 --- /dev/null +++ b/lib/test_kstate.c @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <linux/io.h> +#include <linux/kstate.h> +#include <linux/mm.h> +#include <linux/module.h> + +static unsigned long ulong_val; +struct kstate_test_data { + int i; + unsigned long *p_ulong; + char s[10]; + struct page *page; +}; + +struct kstate_description test_state = { + .name = "test", + .version_id = 1, + .id = KSTATE_TEST_ID, + .state_list = LIST_HEAD_INIT(test_state.state_list), + .fields = (const struct kstate_field[]) { + KSTATE_BASE_TYPE(i, struct kstate_test_data, int), + KSTATE_BASE_TYPE(s, struct kstate_test_data, char [10]), + KSTATE_POINTER(p_ulong, struct kstate_test_data), + KSTATE_PAGE(page, struct kstate_test_data), + KSTATE_END_OF_LIST() + }, +}; + +static struct kstate_test_data test_data; + +static int init_test_data(void) +{ + struct page *page; + int i; + + test_data.i = 10; + ulong_val = 20; + memcpy(test_data.s, "abcdefghk", sizeof(test_data.s)); + page = alloc_page(GFP_KERNEL); + if (!page) + return -ENOMEM; + + for (i = 0; i < PAGE_SIZE/4; i += 4) + *((u32 *)page_address(page) + i) = 0xdeadbeef; + test_data.page = page; + return 0; +} + +static void validate_test_data(void) +{ + int i; + + if (WARN_ON(test_data.i != 10)) + return; + if (WARN_ON(*test_data.p_ulong != 20)) + return; + if (WARN_ON(strcmp(test_data.s, "abcdefghk") != 0)) + return; + + for (i = 0; i < PAGE_SIZE/4; i += 4) { + u32 val = *((u32 *)page_address(test_data.page) + i); + + WARN_ON(val != 0xdeadbeef); + } +} + +static int __init test_kstate_init(void) +{ + int ret = 0; + + test_data.p_ulong = &ulong_val; + + if (!is_kstate_kernel()) { + ret = init_test_data(); + if (ret) + goto out; + } + + kstate_register(&test_state, &test_data); + + validate_test_data(); + +out: + return ret; +} +__initcall(test_kstate_init); -- 2.45.3