Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2c1b4a5c authored by Rafael J. Wysocki's avatar Rafael J. Wysocki Committed by Linus Torvalds
Browse files

[PATCH] swsusp: rework memory freeing on resume



The following patch makes swsusp use the PG_nosave and PG_nosave_free flags to
mark pages that should be freed in case of an error during resume.

This allows us to simplify the code and to use swsusp_free() in all of the
swsusp's resume error paths, which makes them actually work.

Signed-off-by: default avatarRafael J. Wysocki <rjw@sisk.pl>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent a0f49651
Loading
Loading
Loading
Loading
+19 −65
Original line number Original line Diff line number Diff line
@@ -147,57 +147,7 @@ extern int restore_image(void);


pgd_t *temp_level4_pgt;
pgd_t *temp_level4_pgt;


static void **pages;
static int res_phys_pud_init(pud_t *pud, unsigned long address, unsigned long end)

static inline void *__add_page(void)
{
	void **c;

	c = (void **)get_usable_page(GFP_ATOMIC);
	if (c) {
		*c = pages;
		pages = c;
	}
	return c;
}

static inline void *__next_page(void)
{
	void **c;

	c = pages;
	if (c) {
		pages = *c;
		*c = NULL;
	}
	return c;
}

/*
 * Try to allocate as many usable pages as needed and daisy chain them.
 * If one allocation fails, free the pages allocated so far
 */
static int alloc_usable_pages(unsigned long n)
{
	void *p;

	pages = NULL;
	do
		if (!__add_page())
			break;
	while (--n);
	if (n) {
		p = __next_page();
		while (p) {
			free_page((unsigned long)p);
			p = __next_page();
		}
		return -ENOMEM;
	}
	return 0;
}

static void res_phys_pud_init(pud_t *pud, unsigned long address, unsigned long end)
{
{
	long i, j;
	long i, j;


@@ -211,7 +161,9 @@ static void res_phys_pud_init(pud_t *pud, unsigned long address, unsigned long e
		if (paddr >= end)
		if (paddr >= end)
			break;
			break;


		pmd = (pmd_t *)__next_page();
		pmd = (pmd_t *)get_safe_page(GFP_ATOMIC);
		if (!pmd)
			return -ENOMEM;
		set_pud(pud, __pud(__pa(pmd) | _KERNPG_TABLE));
		set_pud(pud, __pud(__pa(pmd) | _KERNPG_TABLE));
		for (j = 0; j < PTRS_PER_PMD; pmd++, j++, paddr += PMD_SIZE) {
		for (j = 0; j < PTRS_PER_PMD; pmd++, j++, paddr += PMD_SIZE) {
			unsigned long pe;
			unsigned long pe;
@@ -223,13 +175,17 @@ static void res_phys_pud_init(pud_t *pud, unsigned long address, unsigned long e
			set_pmd(pmd, __pmd(pe));
			set_pmd(pmd, __pmd(pe));
		}
		}
	}
	}
	return 0;
}
}


static void set_up_temporary_mappings(void)
static int set_up_temporary_mappings(void)
{
{
	unsigned long start, end, next;
	unsigned long start, end, next;
	int error;


	temp_level4_pgt = (pgd_t *)__next_page();
	temp_level4_pgt = (pgd_t *)get_safe_page(GFP_ATOMIC);
	if (!temp_level4_pgt)
		return -ENOMEM;


	/* It is safe to reuse the original kernel mapping */
	/* It is safe to reuse the original kernel mapping */
	set_pgd(temp_level4_pgt + pgd_index(__START_KERNEL_map),
	set_pgd(temp_level4_pgt + pgd_index(__START_KERNEL_map),
@@ -240,29 +196,27 @@ static void set_up_temporary_mappings(void)
	end = (unsigned long)pfn_to_kaddr(end_pfn);
	end = (unsigned long)pfn_to_kaddr(end_pfn);


	for (; start < end; start = next) {
	for (; start < end; start = next) {
		pud_t *pud = (pud_t *)__next_page();
		pud_t *pud = (pud_t *)get_safe_page(GFP_ATOMIC);
		if (!pud)
			return -ENOMEM;
		next = start + PGDIR_SIZE;
		next = start + PGDIR_SIZE;
		if (next > end)
		if (next > end)
			next = end;
			next = end;
		res_phys_pud_init(pud, __pa(start), __pa(next));
		if ((error = res_phys_pud_init(pud, __pa(start), __pa(next))))
			return error;
		set_pgd(temp_level4_pgt + pgd_index(start),
		set_pgd(temp_level4_pgt + pgd_index(start),
			mk_kernel_pgd(__pa(pud)));
			mk_kernel_pgd(__pa(pud)));
	}
	}
	return 0;
}
}


int swsusp_arch_resume(void)
int swsusp_arch_resume(void)
{
{
	unsigned long n;
	int error;


	n = ((end_pfn << PAGE_SHIFT) + PUD_SIZE - 1) >> PUD_SHIFT;
	n += (n + PTRS_PER_PUD - 1) / PTRS_PER_PUD + 1;
	pr_debug("swsusp_arch_resume(): pages needed = %lu\n", n);
	if (alloc_usable_pages(n)) {
		free_eaten_memory();
		return -ENOMEM;
	}
	/* We have got enough memory and from now on we cannot recover */
	/* We have got enough memory and from now on we cannot recover */
	set_up_temporary_mappings();
	if ((error = set_up_temporary_mappings()))
		return error;
	restore_image();
	restore_image();
	return 0;
	return 0;
}
}
+1 −2
Original line number Original line Diff line number Diff line
@@ -71,8 +71,7 @@ void restore_processor_state(void);
struct saved_context;
struct saved_context;
void __save_processor_state(struct saved_context *ctxt);
void __save_processor_state(struct saved_context *ctxt);
void __restore_processor_state(struct saved_context *ctxt);
void __restore_processor_state(struct saved_context *ctxt);
extern unsigned long get_usable_page(gfp_t gfp_mask);
unsigned long get_safe_page(gfp_t gfp_mask);
extern void free_eaten_memory(void);


/*
/*
 * XXX: We try to keep some more pages free so that I/O operations succeed
 * XXX: We try to keep some more pages free so that I/O operations succeed
+7 −7
Original line number Original line Diff line number Diff line
@@ -30,7 +30,6 @@ extern int swsusp_check(void);
extern int swsusp_read(void);
extern int swsusp_read(void);
extern void swsusp_close(void);
extern void swsusp_close(void);
extern int swsusp_resume(void);
extern int swsusp_resume(void);
extern int swsusp_free(void);




static int noresume = 0;
static int noresume = 0;
@@ -252,14 +251,17 @@ static int software_resume(void)


	pr_debug("PM: Reading swsusp image.\n");
	pr_debug("PM: Reading swsusp image.\n");


	if ((error = swsusp_read()))
	if ((error = swsusp_read())) {
		goto Cleanup;
		swsusp_free();
		goto Thaw;
	}


	pr_debug("PM: Preparing devices for restore.\n");
	pr_debug("PM: Preparing devices for restore.\n");


	if ((error = device_suspend(PMSG_FREEZE))) {
	if ((error = device_suspend(PMSG_FREEZE))) {
		printk("Some devices failed to suspend\n");
		printk("Some devices failed to suspend\n");
		goto Free;
		swsusp_free();
		goto Thaw;
	}
	}


	mb();
	mb();
@@ -268,9 +270,7 @@ static int software_resume(void)
	swsusp_resume();
	swsusp_resume();
	pr_debug("PM: Restore failed, recovering.n");
	pr_debug("PM: Restore failed, recovering.n");
	device_resume();
	device_resume();
 Free:
 Thaw:
	swsusp_free();
 Cleanup:
	unprepare_processes();
	unprepare_processes();
 Done:
 Done:
	/* For success case, the suspend path will release the lock */
	/* For success case, the suspend path will release the lock */
+1 −1
Original line number Original line Diff line number Diff line
@@ -66,7 +66,7 @@ extern asmlinkage int swsusp_arch_suspend(void);
extern asmlinkage int swsusp_arch_resume(void);
extern asmlinkage int swsusp_arch_resume(void);


extern int restore_highmem(void);
extern int restore_highmem(void);
extern void free_pagedir(struct pbe *pblist);
extern struct pbe * alloc_pagedir(unsigned nr_pages);
extern struct pbe * alloc_pagedir(unsigned nr_pages);
extern void create_pbe_list(struct pbe *pblist, unsigned nr_pages);
extern void create_pbe_list(struct pbe *pblist, unsigned nr_pages);
extern void swsusp_free(void);
extern int enough_swap(unsigned nr_pages);
extern int enough_swap(unsigned nr_pages);
+1 −1
Original line number Original line Diff line number Diff line
@@ -240,7 +240,7 @@ static void copy_data_pages(struct pbe *pblist)
 *	free_pagedir - free pages allocated with alloc_pagedir()
 *	free_pagedir - free pages allocated with alloc_pagedir()
 */
 */


void free_pagedir(struct pbe *pblist)
static void free_pagedir(struct pbe *pblist)
{
{
	struct pbe *pbe;
	struct pbe *pbe;


Loading