If there is an error half way through KHO memory preservation, we should
rollback and unpreserve everything that is partially preserved.

Co-developed-by: Mike Rapoport (Microsoft) <[email protected]>
Signed-off-by: Mike Rapoport (Microsoft) <[email protected]>
Signed-off-by: Pasha Tatashin <[email protected]>
---
 lib/test_kho.c | 103 +++++++++++++++++++++++++++++++++++--------------
 1 file changed, 73 insertions(+), 30 deletions(-)

diff --git a/lib/test_kho.c b/lib/test_kho.c
index 9f7cfa6ac855..025ea251a186 100644
--- a/lib/test_kho.c
+++ b/lib/test_kho.c
@@ -33,17 +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_save_data(struct kho_test_state *state, void *fdt)
+static void kho_test_unpreserve_data(struct kho_test_state *state)
+{
+       for (int i = 0; i < state->nr_folios_preserved; i++)
+               kho_unpreserve_folio(state->folios[i]);
+
+       kho_unpreserve_vmalloc(&state->folios_info_phys);
+       vfree(state->folios_info);
+}
+
+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)
@@ -51,64 +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 |= kho_preserve_folio(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;
 }
 
@@ -174,14 +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;
 
        return 0;
 
-err_free_fdt:
-       folio_put(state->fdt);
 err_free_folios:
        kvfree(folios);
        return err;
-- 
2.51.1.930.gacf6e81ea2-goog


Reply via email to