[PATCH] [35/48] Suspend2 2.1.9.8 for 2.6.12: 611-io.patch

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



diff -ruNp 612-pagedir.patch-old/kernel/power/suspend2_core/pagedir.c 612-pagedir.patch-new/kernel/power/suspend2_core/pagedir.c
--- 612-pagedir.patch-old/kernel/power/suspend2_core/pagedir.c	1970-01-01 10:00:00.000000000 +1000
+++ 612-pagedir.patch-new/kernel/power/suspend2_core/pagedir.c	2005-07-05 23:48:59.000000000 +1000
@@ -0,0 +1,336 @@
+/*
+ * kernel/power/pagedir.c
+ *
+ * Copyright (C) 1998-2001 Gabor Kuti <[email protected]>
+ * Copyright (C) 1998,2001,2002 Pavel Machek <[email protected]>
+ * Copyright (C) 2002-2003 Florent Chabaud <[email protected]>
+ * Copyright (C) 2002-2005 Nigel Cunningham <[email protected]>
+ *
+ * This file is released under the GPLv2.
+ *
+ * Routines for handling pagesets.
+ * Note that pbes aren't actually stored as such. They're stored as
+ * bitmaps and extents.
+ */
+
+#include <linux/suspend.h>
+#include <linux/highmem.h>
+#include <linux/bootmem.h>
+
+#include "pageflags.h"
+#include "ui.h"
+#include "pagedir.h"
+
+int extra_pagedir_pages_allocated = 0;
+static LIST_HEAD(conflicting_pages);
+
+/* suspend2_free_pagedir_data
+ *
+ * Description:	Free a previously pagedir metadata.
+ */
+void suspend2_free_pagedir_data(void)
+{
+	int pagenumber;
+
+	free_dyn_pageflags(&pageset1_map);
+	free_dyn_pageflags(&pageset2_map);
+	free_dyn_pageflags(&pageset1_copy_map);
+
+	/* Free allocated pages */
+	if (allocd_pages_map) {
+		BITMAP_FOR_EACH_SET(allocd_pages_map, pagenumber) {
+			struct page * page = pfn_to_page(pagenumber);
+			ClearPageNosave(page);
+			__free_pages(page, 0);
+			extra_pagedir_pages_allocated--;
+		}
+		free_dyn_pageflags(&allocd_pages_map);
+	}
+
+	pagedir1.pageset_size = pagedir2.pageset_size = 0;
+}
+
+/* suspend2_allocate_extra_pagedir_memory
+ *
+ * Description:	Allocate memory for making the atomic copy of pagedir1 in the
+ * 		case where it is bigger than pagedir2.
+ * Arguments:	struct pagedir *: 	The pagedir for which we should 
+ * 					allocate memory.
+ * 		int:			Size of pageset 1.
+ * 		int:			Size of pageset 2.
+ * Result:	int. Zero on success. One if unable to allocate enough memory.
+ */
+int suspend2_allocate_extra_pagedir_memory(struct pagedir * p, int pageset_size,
+		int alloc_from)
+{
+	int num_to_alloc = pageset_size - alloc_from - extra_pagedir_pages_allocated;
+	int j, order;
+
+	if (num_to_alloc < 1)
+		num_to_alloc = 0;
+
+	if (num_to_alloc) {
+		int num_added = 0;
+	
+		order = generic_fls(num_to_alloc);
+		if (order >= MAX_ORDER)
+			order = MAX_ORDER - 1;
+
+		while (num_added < num_to_alloc) {
+			struct page * newpage;
+			unsigned long virt;
+			
+			while ((1 << order) > (num_to_alloc - num_added))
+				order--;
+
+			virt = __get_free_pages(GFP_ATOMIC, order);
+			while ((!virt) && (order > 0)) {
+				order--;
+				virt = __get_free_pages(GFP_ATOMIC, order);
+			}
+
+			if (!virt) {
+				p->pageset_size += num_added;
+				return 1;
+			}
+
+			newpage = virt_to_page(virt);
+			for (j = 0; j < (1 << order); j++) {
+				SetPageNosave(newpage + j);
+				/* Pages will be freed one at a time. */
+				set_page_count(newpage + j, 1);
+				SetPageAllocd(newpage + j);
+				extra_pagedir_pages_allocated++;
+			}
+			num_added+= (1 << order);
+		}
+	}
+
+	//p->pageset_size = pageset_size;
+	return 0;
+}
+
+/*
+ * suspend2_mark_task_as_pageset1
+ * Functionality   : Marks all the pages belonging to a given process as
+ *                   pageset 1 pages.
+ * Called From     : pagedir.c - mark_pages_for_pageset2
+ *
+ * This is a builtin to avoid exporting follow_page.
+ */
+void suspend2_mark_task_as_pageset1(struct task_struct *t)
+{
+	struct vm_area_struct *vma;
+	struct mm_struct *mm;
+
+	mm = t->active_mm;
+
+	if (!mm || !mm->mmap) return;
+
+	down_read(&mm->mmap_sem);
+	for (vma = mm->mmap; vma; vma = vma->vm_next) {
+		unsigned long posn;
+
+		if (!vma->vm_start)
+			continue;
+
+		for (posn = vma->vm_start; posn < vma->vm_end; posn += PAGE_SIZE) {
+			struct page *page = follow_page(mm, posn, 0);
+			if (page)
+				ClearPagePageset2(page);
+		}
+	}
+	up_read(&mm->mmap_sem);
+}
+
+/* mark_pages_for_pageset2
+ *
+ * Description:	Mark unshared pages in processes not needed for suspend as
+ * 		being able to be written out in a separate pagedir.
+ * 		HighMem pages are simply marked as pageset2. They won't be
+ * 		needed during suspend.
+ */
+
+void suspend2_mark_pages_for_pageset2(void)
+{
+	struct zone * zone;
+	struct task_struct *p, *g;
+	unsigned long flags;
+	int i;
+
+	clear_dyn_pageflags(pageset2_map);
+
+	/* 
+	 * Note that we don't clear the map to begin with!
+	 * This is because if we eat memory, we loose track
+	 * of LRU pages that are still in use but taken off
+	 * the LRU. If I can figure out how the VM keeps
+	 * track of them, I might be able to tweak this a
+	 * little further and decrease pageset one's size
+	 * further.
+	 *
+	 * (Memory grabbing clears the pageset2 flag on
+	 * pages that are really freed!).
+	 */
+	
+	/* Add LRU pages */
+	for_each_zone(zone) {
+		spin_lock_irqsave(&zone->lru_lock, flags);
+		if (zone->nr_inactive) {
+			struct page * page;
+			list_for_each_entry(page, &zone->inactive_list, lru)
+				SetPagePageset2(page);
+		}
+		if (zone->nr_active) {
+			struct page * page;
+			list_for_each_entry(page, &zone->active_list, lru)
+				SetPagePageset2(page);
+		}
+		spin_unlock_irqrestore(&zone->lru_lock, flags);
+	}
+
+	/* Now we find all userspace process (with task->mm) marked PF_NOFREEZE
+	 * and move them into pageset1.
+	 */
+	read_lock(&tasklist_lock);
+	do_each_thread(g, p) {
+		if ((p->mm || p->active_mm) && (p->flags & PF_NOFREEZE))
+			suspend2_mark_task_as_pageset1(p);
+	} while_each_thread(g, p);
+	read_unlock(&tasklist_lock);
+
+	for (i = 0; i < max_pfn; i++) {
+		struct page * page = pfn_to_page(i);
+		BUG_ON(PagePageset2(page) && PageSlab(page));
+	}
+}
+
+/* suspend2_get_nonconflicting_pages
+ *
+ * Description: Gets higher-order pages that won't be overwritten
+ *		while copying the original pages.
+ *
+ *		Note that if only one of the allocated pages overlaps
+ *		with the pages that overlap, another set must be
+ *		tried. Therefore, you shouldn't use this function
+ *		much, and not with high orders.
+ */
+
+unsigned long suspend2_get_nonconflicting_pages(const int order)
+{
+	struct page * page;
+	unsigned long new_page;
+	int more = 0;
+	unsigned long pgcount;
+
+	do {
+		new_page = __get_free_pages(GFP_ATOMIC | __GFP_NOWARN, order);
+		if (!new_page)
+			return 0;
+		more = 0;
+		for (pgcount = 0; pgcount < (1UL << order); pgcount++) {
+			page = virt_to_page(new_page + PAGE_SIZE * pgcount);
+			if (PagePageset1(page)) {
+				more = 1;
+				break;
+			}
+		}
+		if (more) {
+			page = virt_to_page(new_page);
+			list_add(&page->lru, &conflicting_pages);
+
+			/* since this page is technically free, we can abuse it to
+			 * store the order. When we resume it'll just be overwritten,
+			 * but we need this value when freeing it in
+			 * suspend2_release_conflicting_pages. */
+			*((int*)new_page) = order;
+		}
+	}
+	while (more);
+
+	memset((void*)new_page, 0, PAGE_SIZE * (1<<order));
+	return new_page;
+}
+
+/* suspend2_get_nonconflicting_page
+ *
+ * Description: Gets a page that will not be overwritten as we copy the
+ * 		original kernel page.
+ */
+
+unsigned long suspend2_get_nonconflicting_page(void)
+{
+	return suspend2_get_nonconflicting_pages(0);
+}
+
+/* suspend2_release_conflicting_pages
+ *
+ * Description: Release conflicting pages. If we resume, we don't care (their
+ * 		status will not matter), but if we abort for some reason, they
+ * 		should not leak.
+ */
+
+void suspend2_release_conflicting_pages(void)
+{
+	struct page *this_page, *next;
+	int order;
+
+	list_for_each_entry_safe(this_page, next, &conflicting_pages, lru)
+	{
+		order = *((int*)(page_address(this_page)));
+		__free_pages(virt_to_page(this_page), order);
+	}
+}
+
+/* relocate_page_if_required
+ *
+ * Description: Given the address of a pointer to a page, we check if the page
+ * 		needs relocating and do so if needs be, adjusting the pointer
+ * 		too.
+ */
+
+void suspend2_relocate_page_if_required(void ** page_pointer_addr)
+{
+	void * current_value = *page_pointer_addr;
+	if PagePageset1(virt_to_page(current_value)) {
+		unsigned long * new_page = (unsigned long *) suspend2_get_nonconflicting_page();
+		memcpy(new_page, current_value, PAGE_SIZE);
+		free_pages((unsigned long) current_value, 0);
+		*page_pointer_addr = new_page;
+	}
+}
+
+/* get_pageset1_load_addresses
+ * 
+ * Description: We check here that pagedir & pages it points to won't collide
+ * 		with pages where we're going to restore from the loaded pages
+ * 		later.
+ * Returns:	Zero on success, one if couldn't find enough pages (shouldn't
+ * 		happen).
+ */
+
+int suspend2_get_pageset1_load_addresses(void)
+{
+	int i, nrdone = 0, result = 0;
+	void *this;
+
+	/*
+	 * Because we're trying to make this work when we're saving as much
+	 * memory as possible we need to remember the pages we reject here
+	 * and then free them when we're done.
+	 */
+	
+	for(i=0; i < pagedir1.pageset_size; i++) {
+		this = (void *) suspend2_get_nonconflicting_page();
+		if (!this) {
+			abort_suspend("Error: Ran out of memory seeking locations for reloading data.");
+			result = 1;
+			break;
+		}
+		SetPagePageset1Copy(virt_to_page(this));
+		nrdone++;
+	}
+	suspend2_release_conflicting_pages();
+
+	return result;
+}
diff -ruNp 612-pagedir.patch-old/kernel/power/suspend2_core/pagedir.h 612-pagedir.patch-new/kernel/power/suspend2_core/pagedir.h
--- 612-pagedir.patch-old/kernel/power/suspend2_core/pagedir.h	1970-01-01 10:00:00.000000000 +1000
+++ 612-pagedir.patch-new/kernel/power/suspend2_core/pagedir.h	2005-07-04 23:14:19.000000000 +1000
@@ -0,0 +1,55 @@
+/*
+ * kernel/power/suspend2_core/pagedir.h
+ *
+ * Copyright (C) 2004-2005 Nigel Cunningham <[email protected]>
+ *
+ * This file is released under the GPLv2.
+ *
+ * Declarations for routines for handling pagesets.
+ */
+
+/* Pagedir
+ *
+ * Contains the metadata for a set of pages saved in the image.
+ */
+
+struct pagedir {
+	int pageset_size;
+	int lastpageset_size;
+};
+
+extern struct pagedir pagedir1, pagedir2;
+
+#define pageset1_size (pagedir1.pageset_size)
+#define pageset2_size (pagedir2.pageset_size)
+
+extern void suspend2_copy_pageset1(void);
+
+extern void suspend2_free_pagedir_data(void);
+
+extern int suspend2_allocate_extra_pagedir_memory(struct pagedir * p, int pageset_size, int alloc_from);
+
+extern void suspend2_mark_task_as_pageset1 (struct task_struct *t);
+extern void suspend2_mark_pages_for_pageset2(void);
+
+extern void suspend2_release_conflicting_pages(void);
+extern void suspend2_relocate_page_if_required(void ** page_pointer_addr);
+extern int suspend2_get_pageset1_load_addresses(void);
+
+extern int extra_pagedir_pages_allocated;
+
+#ifdef CONFIG_DEBUG_PAGEALLOC
+/* Returns whether it was already in the requested state */
+extern int suspend2_map_kernel_page(struct page * page, int enable);
+
+extern void suspend2_map_atomic_copy_pages(void);
+extern void suspend2_unmap_atomic_copy_pages(void);
+#else
+#define suspend2_map_atomic_copy_pages() do { } while(0)
+#define suspend2_unmap_atomic_copy_pages() do { } while(0)
+static inline int suspend2_map_kernel_page(struct page * page, int enable)
+{
+	return 1;
+}
+#endif
+

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]
  Powered by Linux