summaryrefslogtreecommitdiff
path: root/lib/test_kho.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/test_kho.c')
-rw-r--r--lib/test_kho.c140
1 files changed, 80 insertions, 60 deletions
diff --git a/lib/test_kho.c b/lib/test_kho.c
index fff018e5548d..47de56280795 100644
--- a/lib/test_kho.c
+++ b/lib/test_kho.c
@@ -33,44 +33,28 @@ struct kho_test_state {
unsigned int nr_folios;
struct folio **folios;
phys_addr_t *folios_info;
+ struct kho_vmalloc folios_info_phys;
+ int nr_folios_preserved;
struct folio *fdt;
__wsum csum;
};
static struct kho_test_state kho_test_state;
-static int kho_test_notifier(struct notifier_block *self, unsigned long cmd,
- void *v)
+static void kho_test_unpreserve_data(struct kho_test_state *state)
{
- struct kho_test_state *state = &kho_test_state;
- struct kho_serialization *ser = v;
- int err = 0;
-
- switch (cmd) {
- case KEXEC_KHO_ABORT:
- return NOTIFY_DONE;
- case KEXEC_KHO_FINALIZE:
- /* Handled below */
- break;
- default:
- return NOTIFY_BAD;
- }
-
- err |= kho_preserve_folio(state->fdt);
- err |= kho_add_subtree(ser, KHO_TEST_FDT, folio_address(state->fdt));
+ for (int i = 0; i < state->nr_folios_preserved; i++)
+ kho_unpreserve_folio(state->folios[i]);
- return err ? NOTIFY_BAD : NOTIFY_DONE;
+ kho_unpreserve_vmalloc(&state->folios_info_phys);
+ vfree(state->folios_info);
}
-static struct notifier_block kho_test_nb = {
- .notifier_call = kho_test_notifier,
-};
-
-static int kho_test_save_data(struct kho_test_state *state, void *fdt)
+static int kho_test_preserve_data(struct kho_test_state *state)
{
- phys_addr_t *folios_info __free(kvfree) = NULL;
struct kho_vmalloc folios_info_phys;
- int err = 0;
+ phys_addr_t *folios_info;
+ int err;
folios_info = vmalloc_array(state->nr_folios, sizeof(*folios_info));
if (!folios_info)
@@ -78,62 +62,98 @@ static int kho_test_save_data(struct kho_test_state *state, void *fdt)
err = kho_preserve_vmalloc(folios_info, &folios_info_phys);
if (err)
- return err;
+ goto err_free_info;
+
+ state->folios_info_phys = folios_info_phys;
+ state->folios_info = folios_info;
for (int i = 0; i < state->nr_folios; i++) {
struct folio *folio = state->folios[i];
unsigned int order = folio_order(folio);
folios_info[i] = virt_to_phys(folio_address(folio)) | order;
-
err = kho_preserve_folio(folio);
if (err)
- break;
+ goto err_unpreserve;
+ state->nr_folios_preserved++;
}
+ return 0;
+
+err_unpreserve:
+ /*
+ * kho_test_unpreserve_data frees folio_info, bail out immediately to
+ * avoid double free
+ */
+ kho_test_unpreserve_data(state);
+ return err;
+
+err_free_info:
+ vfree(folios_info);
+ return err;
+}
+
+static int kho_test_prepare_fdt(struct kho_test_state *state, ssize_t fdt_size)
+{
+ const char compatible[] = KHO_TEST_COMPAT;
+ unsigned int magic = KHO_TEST_MAGIC;
+ void *fdt = folio_address(state->fdt);
+ int err;
+
+ err = fdt_create(fdt, fdt_size);
+ err |= fdt_finish_reservemap(fdt);
+ err |= fdt_begin_node(fdt, "");
+ err |= fdt_property(fdt, "compatible", compatible, sizeof(compatible));
+ err |= fdt_property(fdt, "magic", &magic, sizeof(magic));
+
err |= fdt_begin_node(fdt, "data");
err |= fdt_property(fdt, "nr_folios", &state->nr_folios,
sizeof(state->nr_folios));
- err |= fdt_property(fdt, "folios_info", &folios_info_phys,
- sizeof(folios_info_phys));
+ err |= fdt_property(fdt, "folios_info", &state->folios_info_phys,
+ sizeof(state->folios_info_phys));
err |= fdt_property(fdt, "csum", &state->csum, sizeof(state->csum));
err |= fdt_end_node(fdt);
- if (!err)
- state->folios_info = no_free_ptr(folios_info);
+ err |= fdt_end_node(fdt);
+ err |= fdt_finish(fdt);
return err;
}
-static int kho_test_prepare_fdt(struct kho_test_state *state)
+static int kho_test_preserve(struct kho_test_state *state)
{
- const char compatible[] = KHO_TEST_COMPAT;
- unsigned int magic = KHO_TEST_MAGIC;
ssize_t fdt_size;
- int err = 0;
- void *fdt;
+ int err;
fdt_size = state->nr_folios * sizeof(phys_addr_t) + PAGE_SIZE;
state->fdt = folio_alloc(GFP_KERNEL, get_order(fdt_size));
if (!state->fdt)
return -ENOMEM;
- fdt = folio_address(state->fdt);
-
- err |= fdt_create(fdt, fdt_size);
- err |= fdt_finish_reservemap(fdt);
+ err = kho_preserve_folio(state->fdt);
+ if (err)
+ goto err_free_fdt;
- err |= fdt_begin_node(fdt, "");
- err |= fdt_property(fdt, "compatible", compatible, sizeof(compatible));
- err |= fdt_property(fdt, "magic", &magic, sizeof(magic));
- err |= kho_test_save_data(state, fdt);
- err |= fdt_end_node(fdt);
+ err = kho_test_preserve_data(state);
+ if (err)
+ goto err_unpreserve_fdt;
- err |= fdt_finish(fdt);
+ err = kho_test_prepare_fdt(state, fdt_size);
+ if (err)
+ goto err_unpreserve_data;
+ err = kho_add_subtree(KHO_TEST_FDT, folio_address(state->fdt));
if (err)
- folio_put(state->fdt);
+ goto err_unpreserve_data;
+
+ return 0;
+err_unpreserve_data:
+ kho_test_unpreserve_data(state);
+err_unpreserve_fdt:
+ kho_unpreserve_folio(state->fdt);
+err_free_fdt:
+ folio_put(state->fdt);
return err;
}
@@ -199,18 +219,12 @@ static int kho_test_save(void)
if (err)
goto err_free_folios;
- err = kho_test_prepare_fdt(state);
+ err = kho_test_preserve(state);
if (err)
goto err_free_folios;
- err = register_kho_notifier(&kho_test_nb);
- if (err)
- goto err_free_fdt;
-
return 0;
-err_free_fdt:
- folio_put(state->fdt);
err_free_folios:
kvfree(folios);
return err;
@@ -292,7 +306,6 @@ static int kho_test_restore(phys_addr_t fdt_phys)
if (err)
return err;
- pr_info("KHO restore succeeded\n");
return 0;
}
@@ -305,8 +318,15 @@ static int __init kho_test_init(void)
return 0;
err = kho_retrieve_subtree(KHO_TEST_FDT, &fdt_phys);
- if (!err)
- return kho_test_restore(fdt_phys);
+ if (!err) {
+ err = kho_test_restore(fdt_phys);
+ if (err)
+ pr_err("KHO restore failed\n");
+ else
+ pr_info("KHO restore succeeded\n");
+
+ return err;
+ }
if (err != -ENOENT) {
pr_warn("failed to retrieve %s FDT: %d\n", KHO_TEST_FDT, err);
@@ -329,7 +349,7 @@ static void kho_test_cleanup(void)
static void __exit kho_test_exit(void)
{
- unregister_kho_notifier(&kho_test_nb);
+ kho_remove_subtree(folio_address(kho_test_state.fdt));
kho_test_cleanup();
}
module_exit(kho_test_exit);