| @@ -8,6 +8,7 @@ · 7 unmodified lines |
| 8 | | 8 | |
| 9 | #define pr_fmt(fmt) "KHO: " fmt | 9 | #define pr_fmt(fmt) "KHO: " fmt |
| 10 | | 10 | |
| | 11 | #include <linux/cleanup.h> |
| 11 | #include <linux/cma.h> | 12 | #include <linux/cma.h> |
| 12 | #include <linux/count_zeros.h> | 13 | #include <linux/count_zeros.h> |
| 13 | #include <linux/debugfs.h> | 14 | #include <linux/debugfs.h> |
| @@ -22,6 +23,7 @@ · 8 unmodified lines |
| 22 | | 23 | |
| 23 | #include <asm/early_ioremap.h> | 24 | #include <asm/early_ioremap.h> |
| 24 | | 25 | |
| | 26 | #include "kexec_handover_internal.h" |
| 25 | /* | 27 | /* |
| 26 | * KHO is tightly coupled with mm init and needs access to some of mm | 28 | * KHO is tightly coupled with mm init and needs access to some of mm |
| 27 | * internal APIs. | 29 | * internal APIs. |
| @@ -133,26 +135,26 @@ static struct kho_out kho_out = { · 105 unmodified lines |
| 133 | | 135 | |
| 134 | static void *xa_load_or_alloc(struct xarray *xa, unsigned long index, size_t sz) | 136 | static void *xa_load_or_alloc(struct xarray *xa, unsigned long index, size_t sz) |
| 135 | { | 137 | { |
| 136 | void *elm, *res; | 138 | void *res = xa_load(xa, index); |
| 137 | | 139 | |
| 138 | elm = xa_load(xa, index); | 140 | if (res) |
| 139 | if (elm) | 141 | return res; |
| 140 | return elm; | 142 | |
| | 143 | void *elm __free(kfree) = kzalloc(sz, GFP_KERNEL); |
| 141 | | 144 | |
| 142 | elm = kzalloc(sz, GFP_KERNEL); | | |
| 143 | if (!elm) | 145 | if (!elm) |
| 144 | return ERR_PTR(-ENOMEM); | 146 | return ERR_PTR(-ENOMEM); |
| 145 | | 147 | |
| | 148 | if (WARN_ON(kho_scratch_overlap(virt_to_phys(elm), sz))) |
| | 149 | return ERR_PTR(-EINVAL); |
| | 150 | |
| 146 | res = xa_cmpxchg(xa, index, NULL, elm, GFP_KERNEL); | 151 | res = xa_cmpxchg(xa, index, NULL, elm, GFP_KERNEL); |
| 147 | if (xa_is_err(res)) | 152 | if (xa_is_err(res)) |
| 148 | res = ERR_PTR(xa_err(res)); | 153 | return ERR_PTR(xa_err(res)); |
| 149 | | 154 | else if (res) |
| 150 | if (res) { | | |
| 151 | kfree(elm); | | |
| 152 | return res; | 155 | return res; |
| 153 | } | | |
| 154 | | 156 | |
| 155 | return elm; | 157 | return no_free_ptr(elm); |
| 156 | } | 158 | } |
| 157 | | 159 | |
| 158 | static void __kho_unpreserve(struct kho_mem_track *track, unsigned long pfn, | 160 | static void __kho_unpreserve(struct kho_mem_track *track, unsigned long pfn, |
| @@ -345,15 +347,19 @@ static_assert(sizeof(struct khoser_mem_chunk) == PAGE_SIZE); · 186 unmodified lines |
| 345 | static struct khoser_mem_chunk *new_chunk(struct khoser_mem_chunk *cur_chunk, | 347 | static struct khoser_mem_chunk *new_chunk(struct khoser_mem_chunk *cur_chunk, |
| 346 | unsigned long order) | 348 | unsigned long order) |
| 347 | { | 349 | { |
| 348 | struct khoser_mem_chunk *chunk; | 350 | struct khoser_mem_chunk *chunk __free(kfree) = NULL; |
| 349 | | 351 | |
| 350 | chunk = kzalloc(PAGE_SIZE, GFP_KERNEL); | 352 | chunk = kzalloc(PAGE_SIZE, GFP_KERNEL); |
| 351 | if (!chunk) | 353 | if (!chunk) |
| 352 | return NULL; | 354 | return ERR_PTR(-ENOMEM); |
| | 355 | |
| | 356 | if (WARN_ON(kho_scratch_overlap(virt_to_phys(chunk), PAGE_SIZE))) |
| | 357 | return ERR_PTR(-EINVAL); |
| | 358 | |
| 353 | chunk->hdr.order = order; | 359 | chunk->hdr.order = order; |
| 354 | if (cur_chunk) | 360 | if (cur_chunk) |
| 355 | KHOSER_STORE_PTR(cur_chunk->hdr.next, chunk); | 361 | KHOSER_STORE_PTR(cur_chunk->hdr.next, chunk); |
| 356 | return chunk; | 362 | return no_free_ptr(chunk); |
| 357 | } | 363 | } |
| 358 | | 364 | |
| 359 | static void kho_mem_ser_free(struct khoser_mem_chunk *first_chunk) | 365 | static void kho_mem_ser_free(struct khoser_mem_chunk *first_chunk) |
| @@ -374,14 +380,17 @@ static int kho_mem_serialize(struct kho_serialization *ser) · 14 unmodified lines |
| 374 | struct khoser_mem_chunk *chunk = NULL; | 380 | struct khoser_mem_chunk *chunk = NULL; |
| 375 | struct kho_mem_phys *physxa; | 381 | struct kho_mem_phys *physxa; |
| 376 | unsigned long order; | 382 | unsigned long order; |
| | 383 | int err = -ENOMEM; |
| 377 | | 384 | |
| 378 | xa_for_each(&ser->track.orders, order, physxa) { | 385 | xa_for_each(&ser->track.orders, order, physxa) { |
| 379 | struct kho_mem_phys_bits *bits; | 386 | struct kho_mem_phys_bits *bits; |
| 380 | unsigned long phys; | 387 | unsigned long phys; |
| 381 | | 388 | |
| 382 | chunk = new_chunk(chunk, order); | 389 | chunk = new_chunk(chunk, order); |
| 383 | if (!chunk) | 390 | if (IS_ERR(chunk)) { |
| | 391 | err = PTR_ERR(chunk); |
| 384 | goto err_free; | 392 | goto err_free; |
| | 393 | } |
| 385 | | 394 | |
| 386 | if (!first_chunk) | 395 | if (!first_chunk) |
| 387 | first_chunk = chunk; | 396 | first_chunk = chunk; |
| @@ -391,8 +400,10 @@ static int kho_mem_serialize(struct kho_serialization *ser) · 3 unmodified lines |
| 391 | | 400 | |
| 392 | if (chunk->hdr.num_elms == ARRAY_SIZE(chunk->bitmaps)) { | 401 | if (chunk->hdr.num_elms == ARRAY_SIZE(chunk->bitmaps)) { |
| 393 | chunk = new_chunk(chunk, order); | 402 | chunk = new_chunk(chunk, order); |
| 394 | if (!chunk) | 403 | if (IS_ERR(chunk)) { |
| | 404 | err = PTR_ERR(chunk); |
| 395 | goto err_free; | 405 | goto err_free; |
| | 406 | } |
| 396 | } | 407 | } |
| 397 | | 408 | |
| 398 | elm = &chunk->bitmaps[chunk->hdr.num_elms]; | 409 | elm = &chunk->bitmaps[chunk->hdr.num_elms]; |
| @@ -409,7 +420,7 @@ static int kho_mem_serialize(struct kho_serialization *ser) · 10 unmodified lines |
| 409 | | 420 | |
| 410 | err_free: | 421 | err_free: |
| 411 | kho_mem_ser_free(first_chunk); | 422 | kho_mem_ser_free(first_chunk); |
| 412 | return -ENOMEM; | 423 | return err; |
| 413 | } | 424 | } |
| 414 | | 425 | |
| 415 | static void __init deserialize_bitmap(unsigned int order, | 426 | static void __init deserialize_bitmap(unsigned int order, |
| @@ -465,8 +476,8 @@ static void __init kho_mem_deserialize(const void *fdt) · 49 unmodified lines |
| 465 | * area for early allocations that happen before page allocator is | 476 | * area for early allocations that happen before page allocator is |
| 466 | * initialized. | 477 | * initialized. |
| 467 | */ | 478 | */ |
| 468 | static struct kho_scratch *kho_scratch; | 479 | struct kho_scratch *kho_scratch; |
| 469 | static unsigned int kho_scratch_cnt; | 480 | unsigned int kho_scratch_cnt; |
| 470 | | 481 | |
| 471 | /* | 482 | /* |
| 472 | * The scratch areas are scaled by default as percent of memory allocated from | 483 | * The scratch areas are scaled by default as percent of memory allocated from |
| @@ -752,6 +763,9 @@ int kho_preserve_folio(struct folio *folio) · 279 unmodified lines |
| 752 | const unsigned int order = folio_order(folio); | 763 | const unsigned int order = folio_order(folio); |
| 753 | struct kho_mem_track *track = &kho_out.ser.track; | 764 | struct kho_mem_track *track = &kho_out.ser.track; |
| 754 | | 765 | |
| | 766 | if (WARN_ON(kho_scratch_overlap(pfn << PAGE_SHIFT, PAGE_SIZE << order))) |
| | 767 | return -EINVAL; |
| | 768 | |
| 755 | return __kho_preserve_order(track, pfn, order); | 769 | return __kho_preserve_order(track, pfn, order); |
| 756 | } | 770 | } |
| 757 | EXPORT_SYMBOL_GPL(kho_preserve_folio); | 771 | EXPORT_SYMBOL_GPL(kho_preserve_folio); |
| @@ -775,6 +789,11 @@ int kho_preserve_pages(struct page *page, unsigned int nr_pages) · 17 unmodified lines |
| 775 | unsigned long failed_pfn = 0; | 789 | unsigned long failed_pfn = 0; |
| 776 | int err = 0; | 790 | int err = 0; |
| 777 | | 791 | |
| | 792 | if (WARN_ON(kho_scratch_overlap(start_pfn << PAGE_SHIFT, |
| | 793 | nr_pages << PAGE_SHIFT))) { |
| | 794 | return -EINVAL; |
| | 795 | } |
| | 796 | |
| 778 | while (pfn < end_pfn) { | 797 | while (pfn < end_pfn) { |
| 779 | const unsigned int order = | 798 | const unsigned int order = |
| 780 | min(count_trailing_zeros(pfn), ilog2(end_pfn - pfn)); | 799 | min(count_trailing_zeros(pfn), ilog2(end_pfn - pfn)); |