Re: [PATCH 01/11] kexec: introduce kexec_ops struct
From: Konrad Rzeszutek Wilk
Date: Fri Sep 28 2012 - 11:59:45 EST
On Thu, Sep 27, 2012 at 08:06:28PM +0200, Daniel Kiper wrote:
> Some kexec/kdump implementations (e.g. Xen PVOPS) on different archs could
> not use default functions or require some changes in behavior of kexec/kdump
> generic code. To cope with that problem kexec_ops struct was introduced.
> It allows a developer to replace all or some functions and control some
> functionality of kexec/kdump generic code.
>
> Default behavior of kexec/kdump generic code is not changed.
>
> Signed-off-by: Daniel Kiper <daniel.kiper@xxxxxxxxxx>
> ---
> include/linux/kexec.h | 18 +++++++
> kernel/kexec.c | 125 ++++++++++++++++++++++++++++++++++++-------------
> 2 files changed, 111 insertions(+), 32 deletions(-)
>
> diff --git a/include/linux/kexec.h b/include/linux/kexec.h
> index 37c5f72..beb08ca 100644
> --- a/include/linux/kexec.h
> +++ b/include/linux/kexec.h
> @@ -165,7 +165,25 @@ struct kimage {
> #endif
> };
>
> +struct kexec_ops {
> + bool always_use_normal_alloc;
So most of these are self-explanatory. But the bool is not that clear
to me. Could you include a documentation comment explaining
its purpose and its implications?
> + struct page *(*kimage_alloc_pages)(gfp_t gfp_mask,
> + unsigned int order,
> + unsigned long limit);
> + void (*kimage_free_pages)(struct page *page);
> + unsigned long (*page_to_pfn)(struct page *page);
> + struct page *(*pfn_to_page)(unsigned long pfn);
> + unsigned long (*virt_to_phys)(volatile void *address);
> + void *(*phys_to_virt)(unsigned long address);
> + int (*machine_kexec_prepare)(struct kimage *image);
> + int (*machine_kexec_load)(struct kimage *image);
> + void (*machine_kexec_cleanup)(struct kimage *image);
> + void (*machine_kexec_unload)(struct kimage *image);
> + void (*machine_kexec_shutdown)(void);
> + void (*machine_kexec)(struct kimage *image);
> +};
>
> +extern struct kexec_ops kexec_ops;
Is this neccessary?
>
> /* kexec interface functions */
> extern void machine_kexec(struct kimage *image);
> diff --git a/kernel/kexec.c b/kernel/kexec.c
> index 0668d58..98556f3 100644
> --- a/kernel/kexec.c
> +++ b/kernel/kexec.c
> @@ -56,6 +56,47 @@ struct resource crashk_res = {
> .flags = IORESOURCE_BUSY | IORESOURCE_MEM
> };
>
> +static struct page *kimage_alloc_pages(gfp_t gfp_mask,
> + unsigned int order,
> + unsigned long limit);
> +static void kimage_free_pages(struct page *page);
> +
> +static unsigned long generic_page_to_pfn(struct page *page)
> +{
> + return page_to_pfn(page);
> +}
> +
> +static struct page *generic_pfn_to_page(unsigned long pfn)
> +{
> + return pfn_to_page(pfn);
> +}
> +
> +static unsigned long generic_virt_to_phys(volatile void *address)
> +{
> + return virt_to_phys(address);
> +}
> +
> +static void *generic_phys_to_virt(unsigned long address)
> +{
> + return phys_to_virt(address);
> +}
> +
> +struct kexec_ops kexec_ops = {
> + .always_use_normal_alloc = false,
> + .kimage_alloc_pages = kimage_alloc_pages,
> + .kimage_free_pages = kimage_free_pages,
> + .page_to_pfn = generic_page_to_pfn,
> + .pfn_to_page = generic_pfn_to_page,
> + .virt_to_phys = generic_virt_to_phys,
> + .phys_to_virt = generic_phys_to_virt,
> + .machine_kexec_prepare = machine_kexec_prepare,
> + .machine_kexec_load = NULL,
Instead of NULL should they just point to some nop function?
> + .machine_kexec_cleanup = machine_kexec_cleanup,
> + .machine_kexec_unload = NULL,
> + .machine_kexec_shutdown = machine_shutdown,
> + .machine_kexec = machine_kexec
> +};
> +
> int kexec_should_crash(struct task_struct *p)
> {
> if (in_interrupt() || !p->pid || is_global_init(p) || panic_on_oops)
> @@ -355,7 +396,9 @@ static int kimage_is_destination_range(struct kimage *image,
> return 0;
> }
>
> -static struct page *kimage_alloc_pages(gfp_t gfp_mask, unsigned int order)
> +static struct page *kimage_alloc_pages(gfp_t gfp_mask,
> + unsigned int order,
> + unsigned long limit)
> {
> struct page *pages;
>
> @@ -392,7 +435,7 @@ static void kimage_free_page_list(struct list_head *list)
>
> page = list_entry(pos, struct page, lru);
> list_del(&page->lru);
> - kimage_free_pages(page);
> + (*kexec_ops.kimage_free_pages)(page);
> }
> }
>
> @@ -425,10 +468,11 @@ static struct page *kimage_alloc_normal_control_pages(struct kimage *image,
> do {
> unsigned long pfn, epfn, addr, eaddr;
>
> - pages = kimage_alloc_pages(GFP_KERNEL, order);
> + pages = (*kexec_ops.kimage_alloc_pages)(GFP_KERNEL, order,
> + KEXEC_CONTROL_MEMORY_LIMIT);
> if (!pages)
> break;
> - pfn = page_to_pfn(pages);
> + pfn = (*kexec_ops.page_to_pfn)(pages);
> epfn = pfn + count;
> addr = pfn << PAGE_SHIFT;
> eaddr = epfn << PAGE_SHIFT;
> @@ -515,7 +559,7 @@ static struct page *kimage_alloc_crash_control_pages(struct kimage *image,
> }
> /* If I don't overlap any segments I have found my hole! */
> if (i == image->nr_segments) {
> - pages = pfn_to_page(hole_start >> PAGE_SHIFT);
> + pages = (*kexec_ops.pfn_to_page)(hole_start >> PAGE_SHIFT);
> break;
> }
> }
> @@ -532,12 +576,13 @@ struct page *kimage_alloc_control_pages(struct kimage *image,
> struct page *pages = NULL;
>
> switch (image->type) {
> + case KEXEC_TYPE_CRASH:
> + if (!kexec_ops.always_use_normal_alloc) {
> + pages = kimage_alloc_crash_control_pages(image, order);
> + break;
> + }
> case KEXEC_TYPE_DEFAULT:
> pages = kimage_alloc_normal_control_pages(image, order);
> - break;
> - case KEXEC_TYPE_CRASH:
> - pages = kimage_alloc_crash_control_pages(image, order);
> - break;
> }
>
> return pages;
> @@ -557,7 +602,7 @@ static int kimage_add_entry(struct kimage *image, kimage_entry_t entry)
> return -ENOMEM;
>
> ind_page = page_address(page);
> - *image->entry = virt_to_phys(ind_page) | IND_INDIRECTION;
> + *image->entry = (*kexec_ops.virt_to_phys)(ind_page) | IND_INDIRECTION;
> image->entry = ind_page;
> image->last_entry = ind_page +
> ((PAGE_SIZE/sizeof(kimage_entry_t)) - 1);
> @@ -616,14 +661,14 @@ static void kimage_terminate(struct kimage *image)
> #define for_each_kimage_entry(image, ptr, entry) \
> for (ptr = &image->head; (entry = *ptr) && !(entry & IND_DONE); \
> ptr = (entry & IND_INDIRECTION)? \
> - phys_to_virt((entry & PAGE_MASK)): ptr +1)
> + (*kexec_ops.phys_to_virt)((entry & PAGE_MASK)): ptr +1)
>
> static void kimage_free_entry(kimage_entry_t entry)
> {
> struct page *page;
>
> - page = pfn_to_page(entry >> PAGE_SHIFT);
> - kimage_free_pages(page);
> + page = (*kexec_ops.pfn_to_page)(entry >> PAGE_SHIFT);
> + (*kexec_ops.kimage_free_pages)(page);
> }
>
> static void kimage_free(struct kimage *image)
> @@ -653,7 +698,7 @@ static void kimage_free(struct kimage *image)
> kimage_free_entry(ind);
>
> /* Handle any machine specific cleanup */
> - machine_kexec_cleanup(image);
> + (*kexec_ops.machine_kexec_cleanup)(image);
>
> /* Free the kexec control pages... */
> kimage_free_page_list(&image->control_pages);
> @@ -709,7 +754,7 @@ static struct page *kimage_alloc_page(struct kimage *image,
> * have a match.
> */
> list_for_each_entry(page, &image->dest_pages, lru) {
> - addr = page_to_pfn(page) << PAGE_SHIFT;
> + addr = (*kexec_ops.page_to_pfn)(page) << PAGE_SHIFT;
> if (addr == destination) {
> list_del(&page->lru);
> return page;
> @@ -720,16 +765,17 @@ static struct page *kimage_alloc_page(struct kimage *image,
> kimage_entry_t *old;
>
> /* Allocate a page, if we run out of memory give up */
> - page = kimage_alloc_pages(gfp_mask, 0);
> + page = (*kexec_ops.kimage_alloc_pages)(gfp_mask, 0,
> + KEXEC_SOURCE_MEMORY_LIMIT);
> if (!page)
> return NULL;
> /* If the page cannot be used file it away */
> - if (page_to_pfn(page) >
> + if ((*kexec_ops.page_to_pfn)(page) >
> (KEXEC_SOURCE_MEMORY_LIMIT >> PAGE_SHIFT)) {
> list_add(&page->lru, &image->unuseable_pages);
> continue;
> }
> - addr = page_to_pfn(page) << PAGE_SHIFT;
> + addr = (*kexec_ops.page_to_pfn)(page) << PAGE_SHIFT;
>
> /* If it is the destination page we want use it */
> if (addr == destination)
> @@ -752,7 +798,7 @@ static struct page *kimage_alloc_page(struct kimage *image,
> struct page *old_page;
>
> old_addr = *old & PAGE_MASK;
> - old_page = pfn_to_page(old_addr >> PAGE_SHIFT);
> + old_page = (*kexec_ops.pfn_to_page)(old_addr >> PAGE_SHIFT);
> copy_highpage(page, old_page);
> *old = addr | (*old & ~PAGE_MASK);
>
> @@ -762,7 +808,7 @@ static struct page *kimage_alloc_page(struct kimage *image,
> */
> if (!(gfp_mask & __GFP_HIGHMEM) &&
> PageHighMem(old_page)) {
> - kimage_free_pages(old_page);
> + (*kexec_ops.kimage_free_pages)(old_page);
> continue;
> }
> addr = old_addr;
> @@ -808,7 +854,7 @@ static int kimage_load_normal_segment(struct kimage *image,
> result = -ENOMEM;
> goto out;
> }
> - result = kimage_add_page(image, page_to_pfn(page)
> + result = kimage_add_page(image, (*kexec_ops.page_to_pfn)(page)
> << PAGE_SHIFT);
> if (result < 0)
> goto out;
> @@ -862,7 +908,7 @@ static int kimage_load_crash_segment(struct kimage *image,
> char *ptr;
> size_t uchunk, mchunk;
>
> - page = pfn_to_page(maddr >> PAGE_SHIFT);
> + page = (*kexec_ops.pfn_to_page)(maddr >> PAGE_SHIFT);
> if (!page) {
> result = -ENOMEM;
> goto out;
> @@ -901,12 +947,13 @@ static int kimage_load_segment(struct kimage *image,
> int result = -ENOMEM;
>
> switch (image->type) {
> + case KEXEC_TYPE_CRASH:
> + if (!kexec_ops.always_use_normal_alloc) {
> + result = kimage_load_crash_segment(image, segment);
> + break;
> + }
> case KEXEC_TYPE_DEFAULT:
> result = kimage_load_normal_segment(image, segment);
> - break;
> - case KEXEC_TYPE_CRASH:
> - result = kimage_load_crash_segment(image, segment);
> - break;
> }
>
> return result;
> @@ -994,6 +1041,8 @@ SYSCALL_DEFINE4(kexec_load, unsigned long, entry, unsigned long, nr_segments,
> /* Free any current crash dump kernel before
> * we corrupt it.
> */
> + if (kexec_ops.machine_kexec_unload)
> + (*kexec_ops.machine_kexec_unload)(image);
> kimage_free(xchg(&kexec_crash_image, NULL));
> result = kimage_crash_alloc(&image, entry,
> nr_segments, segments);
> @@ -1004,7 +1053,7 @@ SYSCALL_DEFINE4(kexec_load, unsigned long, entry, unsigned long, nr_segments,
>
> if (flags & KEXEC_PRESERVE_CONTEXT)
> image->preserve_context = 1;
> - result = machine_kexec_prepare(image);
> + result = (*kexec_ops.machine_kexec_prepare)(image);
> if (result)
> goto out;
>
> @@ -1017,11 +1066,23 @@ SYSCALL_DEFINE4(kexec_load, unsigned long, entry, unsigned long, nr_segments,
> if (flags & KEXEC_ON_CRASH)
> crash_unmap_reserved_pages();
> }
> +
> + if (kexec_ops.machine_kexec_load) {
> + result = (*kexec_ops.machine_kexec_load)(image);
> +
> + if (result)
> + goto out;
> + }
> +
> /* Install the new kernel, and Uninstall the old */
> image = xchg(dest_image, image);
>
> out:
> mutex_unlock(&kexec_mutex);
> +
> + if (kexec_ops.machine_kexec_unload)
> + (*kexec_ops.machine_kexec_unload)(image);
> +
> kimage_free(image);
>
> return result;
> @@ -1095,7 +1156,7 @@ void crash_kexec(struct pt_regs *regs)
> crash_setup_regs(&fixed_regs, regs);
> crash_save_vmcoreinfo();
> machine_crash_shutdown(&fixed_regs);
> - machine_kexec(kexec_crash_image);
> + (*kexec_ops.machine_kexec)(kexec_crash_image);
> }
> mutex_unlock(&kexec_mutex);
> }
> @@ -1117,8 +1178,8 @@ void __weak crash_free_reserved_phys_range(unsigned long begin,
> unsigned long addr;
>
> for (addr = begin; addr < end; addr += PAGE_SIZE) {
> - ClearPageReserved(pfn_to_page(addr >> PAGE_SHIFT));
> - init_page_count(pfn_to_page(addr >> PAGE_SHIFT));
> + ClearPageReserved((*kexec_ops.pfn_to_page)(addr >> PAGE_SHIFT));
> + init_page_count((*kexec_ops.pfn_to_page)(addr >> PAGE_SHIFT));
> free_page((unsigned long)__va(addr));
> totalram_pages++;
> }
> @@ -1572,10 +1633,10 @@ int kernel_kexec(void)
> {
> kernel_restart_prepare(NULL);
> printk(KERN_EMERG "Starting new kernel\n");
> - machine_shutdown();
> + (*kexec_ops.machine_kexec_shutdown)();
> }
>
> - machine_kexec(kexec_image);
> + (*kexec_ops.machine_kexec)(kexec_image);
>
> #ifdef CONFIG_KEXEC_JUMP
> if (kexec_image->preserve_context) {
> --
> 1.5.6.5
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/