public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH] Software Suspend split to two stage V2.
@ 2004-11-19 19:40 hugang
  2004-11-20  0:15 ` Pavel Machek
  2004-11-20  0:30 ` swsusp bigdiff [was Re: [PATCH] Software Suspend split to two stage V2.] Pavel Machek
  0 siblings, 2 replies; 46+ messages in thread
From: hugang @ 2004-11-19 19:40 UTC (permalink / raw)
  To: Pavel Machek; +Cc: linux-kernel

Hi Pavel Machek:

  This patch using pagemap for PageSet2 bitmap, It increase suspend
  speed, In my PowerPC suspend only need 5 secs, cool. 

  Test passed in my ppc and x86 laptop.

  ppc swsusp patch for 2.6.9
   http://honk.physik.uni-konstanz.de/~agx/linux-ppc/kernel/
  Have fun.

diff -ur linux-2.6.9/kernel/power/disk.c linux-2.6.9-hg/kernel/power/disk.c
--- linux-2.6.9/kernel/power/disk.c	2004-10-20 16:00:53.000000000 +0800
+++ linux-2.6.9-hg/kernel/power/disk.c	2004-11-20 01:07:09.000000000 +0800
@@ -17,7 +17,6 @@
 #include <linux/fs.h>
 #include "power.h"
 
-
 extern u32 pm_disk_mode;
 extern struct pm_ops * pm_ops;
 
@@ -26,7 +25,7 @@
 extern int swsusp_read(void);
 extern int swsusp_resume(void);
 extern int swsusp_free(void);
-
+extern int pcs_suspend(int resume);
 
 static int noresume = 0;
 char resume_file[256] = CONFIG_PM_STD_PARTITION;
@@ -73,7 +72,7 @@
 
 static int in_suspend __nosavedata = 0;
 
-
+#if 0
 /**
  *	free_some_memory -  Try to free as much memory as possible
  *
@@ -91,7 +90,7 @@
 	printk("|\n");
 }
 
-
+#endif
 static inline void platform_finish(void)
 {
 	if (pm_disk_mode == PM_DISK_PLATFORM) {
@@ -104,13 +103,14 @@
 {
 	device_resume();
 	platform_finish();
+	pcs_suspend(2);
 	enable_nonboot_cpus();
 	thaw_processes();
 	pm_restore_console();
 }
 
 
-static int prepare(void)
+static int prepare(int resume)
 {
 	int error;
 
@@ -130,9 +130,12 @@
 	}
 
 	/* Free memory before shutting down devices. */
-	free_some_memory();
+	//free_some_memory();
 
 	disable_nonboot_cpus();
+	if ((error = pcs_suspend(resume))) {
+		goto Finish;
+	}
 	if ((error = device_suspend(PM_SUSPEND_DISK)))
 		goto Finish;
 
@@ -160,7 +163,7 @@
 {
 	int error;
 
-	if ((error = prepare()))
+	if ((error = prepare(0)))
 		return error;
 
 	pr_debug("PM: Attempting to suspend to disk.\n");
@@ -226,7 +229,7 @@
 
 	pr_debug("PM: Preparing system for restore.\n");
 
-	if ((error = prepare()))
+	if ((error = prepare(1)))
 		goto Free;
 
 	barrier();
diff -ur linux-2.6.9/kernel/power/process.c linux-2.6.9-hg/kernel/power/process.c
--- linux-2.6.9/kernel/power/process.c	2004-10-20 16:00:53.000000000 +0800
+++ linux-2.6.9-hg/kernel/power/process.c	2004-11-20 01:20:31.000000000 +0800
@@ -4,8 +4,6 @@
  *
  * Originally from swsusp.
  */
-
-
 #undef DEBUG
 
 #include <linux/smp_lock.h>
diff -ur linux-2.6.9/kernel/power/swsusp.c linux-2.6.9-hg/kernel/power/swsusp.c
--- linux-2.6.9/kernel/power/swsusp.c	2004-10-20 16:00:53.000000000 +0800
+++ linux-2.6.9-hg/kernel/power/swsusp.c	2004-11-20 03:21:47.000000000 +0800
@@ -301,6 +301,12 @@
 			printk( "." );
 		error = write_page((pagedir_nosave+i)->address,
 					  &((pagedir_nosave+i)->swap_address));
+#ifdef PCS_DEBUG
+		pr_debug("data_write: %p %p %u\n", 
+				(void *)(pagedir_nosave+i)->address, 
+				(void *)(pagedir_nosave+i)->orig_address,
+				(pagedir_nosave+i)->swap_address);
+#endif
 	}
 	printk(" %d Pages done.\n",i);
 	return error;
@@ -505,6 +511,316 @@
 	return 0;
 }
 
+/**
+ *	calc_order - Determine the order of allocation needed for pagedir_save.
+ *
+ *	This looks tricky, but is just subtle. Please fix it some time.
+ *	Since there are %nr_copy_pages worth of pages in the snapshot, we need
+ *	to allocate enough contiguous space to hold 
+ *		(%nr_copy_pages * sizeof(struct pbe)), 
+ *	which has the saved/orig locations of the page.. 
+ *
+ *	SUSPEND_PD_PAGES() tells us how many pages we need to hold those 
+ *	structures, then we call get_bitmask_order(), which will tell us the
+ *	last bit set in the number, starting with 1. (If we need 30 pages, that
+ *	is 0x0000001e in hex. The last bit is the 5th, which is the order we 
+ *	would use to allocate 32 contiguous pages).
+ *
+ *	Since we also need to save those pages, we add the number of pages that
+ *	we need to nr_copy_pages, and in case of an overflow, do the 
+ *	calculation again to update the number of pages needed. 
+ *
+ *	With this model, we will tend to waste a lot of memory if we just cross
+ *	an order boundary. Plus, the higher the order of allocation that we try
+ *	to do, the more likely we are to fail in a low-memory situtation 
+ *	(though	we're unlikely to get this far in such a case, since swsusp 
+ *	requires half of memory to be free anyway).
+ */
+
+static void calc_order(int *po, int *nr)
+{
+	int diff = 0;
+	int order = 0;
+
+	do {
+		diff = get_bitmask_order(SUSPEND_PD_PAGES(*nr)) - order;
+		if (diff) {
+			order += diff;
+			*nr += 1 << diff;
+		}
+	} while(diff);
+	*po = order;
+}
+
+typedef int (*do_page_t)(struct page *page, void *p);
+
+static int foreach_zone_page(struct zone *zone, do_page_t fun, void *p)
+{
+	unsigned long flags;
+	int inactive = 0, active = 0;
+
+	spin_lock_irqsave(&zone->lru_lock, flags);
+	if (zone->nr_inactive) {
+		struct list_head * entry = zone->inactive_list.prev;
+		while (entry != &zone->inactive_list) {
+			if (fun) {
+				struct page * page = list_entry(entry, struct page, lru);
+				inactive += fun(page, p);
+			} else { 
+				inactive ++;
+			}
+			entry = entry->prev;
+		}
+	}
+	if (zone->nr_active) {
+		struct list_head * entry = zone->active_list.prev;
+		while (entry != &zone->active_list) {
+			if (fun) {
+				struct page * page = list_entry(entry, struct page, lru);
+				active += fun(page, p);
+			} else {
+				active ++;
+			}
+			entry = entry->prev;
+		}
+	}
+	spin_unlock_irqrestore(&zone->lru_lock, flags);
+
+	return (active + inactive);
+}
+
+static unsigned long *pageset2map = NULL;
+
+#define PAGENUMBER(page) (page-mem_map)
+#define PAGEINDEX(page) ((PAGENUMBER(page))/(8*sizeof(unsigned long)))
+#define PAGEBIT(page) ((int) ((PAGENUMBER(page))%(8 * sizeof(unsigned long))))
+
+#define BITS_PER_PAGE (PAGE_SIZE * 8)
+#define PAGES_PER_BITMAP ((max_mapnr + BITS_PER_PAGE - 1) / BITS_PER_PAGE)
+#define BITMAP_ORDER (get_bitmask_order((PAGES_PER_BITMAP) - 1))
+
+#define PagePageset2(page) \
+	test_bit(PAGEBIT(page), &pageset2map[PAGEINDEX(page)])
+#define SetPagePageset2(page) \
+	set_bit(PAGEBIT(page), &pageset2map[PAGEINDEX(page)])
+
+static int setup_pcs_pe(struct page *page, void *p)
+{
+	suspend_pagedir_t **pe = p;
+	unsigned long pfn = page_to_pfn(page);
+
+	BUG_ON(PageReserved(page) && PageNosave(page));
+	if (!pfn_valid(pfn)) {
+		printk("not valid page\n");
+		return 0;
+	}
+	if (PageNosave(page)) {
+		printk("nosave\n");
+		return 0;
+	}
+	if (PageReserved(page) /*&& pfn_is_nosave(pfn)*/) {
+		printk("[nosave]\n");
+		return 0;
+	}
+	if (PageSlab(page)) {
+		printk("slab\n");
+		return (0);
+	}
+	if (pe && *pe) {
+		(*pe)->address = (long) page_address(page);
+		(*pe) ++;
+	}
+	SetPagePageset2(page);
+
+	return (1);
+}
+
+
+static int count_pcs(struct zone *zone, suspend_pagedir_t **pe)
+{
+	return foreach_zone_page(zone, setup_pcs_pe, pe);	
+}
+#if 0
+static int comp_pcs_page(struct page *page, void *p)
+{
+	struct page *pg = p;
+	
+	if (pg == page) return (1);
+	else return (0);
+}
+#endif
+
+static int find_pcs(struct zone *zone, struct page *pg)
+{
+	return PagePageset2(pg);
+	/* return foreach_zone_page(zone, comp_pcs_page, pg); */
+}
+
+static suspend_pagedir_t *pagedir_cache = NULL;
+static int nr_copy_pcs = 0;
+static int pcs_order = 0;
+
+static int alloc_pagedir_cache(void)
+{
+	int need_nr_copy_pcs = nr_copy_pcs;
+
+	calc_order(&pcs_order, &need_nr_copy_pcs);
+	pagedir_cache = (suspend_pagedir_t *)
+		__get_free_pages(GFP_ATOMIC | __GFP_COLD, pcs_order);
+	if (!pagedir_cache)
+		return -ENOMEM;
+	memset(pagedir_cache, 0, (1 << pcs_order) * PAGE_SIZE);
+
+	pr_debug("alloc pcs %p, %d\n", pagedir_cache, pcs_order);
+
+	return 0;
+}
+
+static int pcs_alloc_pagemap(void) 
+{
+	pageset2map = (unsigned long *) 
+		__get_free_pages(GFP_ATOMIC | __GFP_COLD, BITMAP_ORDER);
+	memset(pageset2map, 0, (1 << BITMAP_ORDER) * PAGE_SIZE);
+
+	return 0;
+}
+
+static int pcs_free_pagemap(void)
+{
+	if (pageset2map) {
+		free_pages((unsigned long) pageset2map, BITMAP_ORDER);
+		pageset2map = NULL;
+	}
+
+	return (0);
+}
+
+int bio_read_page(pgoff_t page_off, void * page);
+
+static int pcs_read(void)
+{
+	struct pbe * p;
+	int error = 0, i;
+	swp_entry_t entry;
+
+	printk( "Reading Page Caches (%d pages): ", nr_copy_pcs);
+	for(i = 0, p = pagedir_cache; i < nr_copy_pcs && !error; i++, p++) {
+		if (!(i%100))
+			printk( "." );
+		error = bio_read_page(swp_offset(p->swap_address),
+				(void *)p->address);
+#ifdef PCS_DEBUG
+		pr_debug("pcs_read: %p %p %u\n", 
+				(void *)p->address, (void *)p->orig_address, 
+				swp_offset(p->swap_address));
+#endif
+	}
+
+	for (i = 0; i < nr_copy_pcs; i++) {
+		entry = (pagedir_cache + i)->swap_address;
+		if (entry.val)
+			swap_free(entry);
+	}
+	free_pages((unsigned long)pagedir_cache, pcs_order);
+
+	printk(" %d done.\n",i);
+
+	return (0);
+}
+
+static int pcs_write(void)
+{
+	int error = 0;
+	int i;
+
+	printk( "Writing PageCaches to swap (%d pages): ", nr_copy_pcs);
+	for (i = 0; i < nr_copy_pcs && !error; i++) {
+		if (!(i%100))
+			printk( "." );
+		error = write_page((pagedir_cache+i)->address,
+					  &((pagedir_cache+i)->swap_address));
+#ifdef PCS_DEBUG
+		pr_debug("pcs_write: %p %p %u\n", 
+				(void *)(pagedir_cache+i)->address, 
+				(void *)(pagedir_cache+i)->orig_address,
+				(pagedir_cache+i)->swap_address);
+#endif
+	}
+	printk(" %d Pages done.\n",i);
+
+	return error;
+}
+
+static void count_data_pages(void);
+static int swsusp_alloc(void);
+
+int pcs_suspend(int resume)
+{
+	struct zone *zone;
+	suspend_pagedir_t *pe = NULL;
+	int error;
+
+	if (resume == 1) {
+		return (0);
+	}
+	if (resume == 2) {
+		pcs_read();
+		pcs_free_pagemap();
+		return (0);
+	}
+
+	nr_copy_pcs = 0;
+
+	pcs_alloc_pagemap();
+
+	drain_local_pages();
+	for_each_zone(zone) {
+		if (!is_highmem(zone)) {
+			nr_copy_pcs += count_pcs(zone, NULL);
+		}
+	}
+
+	printk("swsusp: Need to copy %u pcs\n", nr_copy_pcs);
+
+	if (nr_copy_pcs == 0) {
+		return (0);
+	}
+
+	if ((error = swsusp_swap_check()))
+		return error;
+
+	if ((error = alloc_pagedir_cache())) {
+		return error;
+	}
+
+	drain_local_pages();
+	count_data_pages();
+	printk("swsusp(1/2): Need to copy %u pages, %u pcs\n",
+			nr_copy_pages, nr_copy_pcs);
+
+	error = swsusp_alloc();
+	if (error)
+		return error;
+	
+	drain_local_pages();
+	count_data_pages();
+	printk("swsusp(2/2): Need to copy %u pages, %u pcs\n",
+			nr_copy_pages, nr_copy_pcs);
+
+	pe = pagedir_cache;
+
+	drain_local_pages();
+	for_each_zone(zone) {
+		if (!is_highmem(zone)) {
+			count_pcs(zone, &pe);
+		}
+	}
+	error = pcs_write();
+	if (error) 
+		return error;
+
+	return (0);
+}
 
 static int pfn_is_nosave(unsigned long pfn)
 {
@@ -547,7 +863,10 @@
 		*zone_pfn += chunk_size - 1;
 		return 0;
 	}
-
+	if ((zone->nr_inactive || zone->nr_active) && 
+			find_pcs(zone, page)) {
+		return 0;
+	}
 	return 1;
 }
 
@@ -557,9 +876,12 @@
 	unsigned long zone_pfn;
 
 	nr_copy_pages = 0;
+	nr_copy_pcs = 0;
 
 	for_each_zone(zone) {
 		if (!is_highmem(zone)) {
+			if (zone->nr_inactive || zone->nr_active)
+				nr_copy_pcs += count_pcs(zone, NULL);
 			for (zone_pfn = 0; zone_pfn < zone->spanned_pages; ++zone_pfn)
 				nr_copy_pages += saveable(zone, &zone_pfn);
 		}
@@ -621,47 +943,6 @@
 }
 
 
-/**
- *	calc_order - Determine the order of allocation needed for pagedir_save.
- *
- *	This looks tricky, but is just subtle. Please fix it some time.
- *	Since there are %nr_copy_pages worth of pages in the snapshot, we need
- *	to allocate enough contiguous space to hold 
- *		(%nr_copy_pages * sizeof(struct pbe)), 
- *	which has the saved/orig locations of the page.. 
- *
- *	SUSPEND_PD_PAGES() tells us how many pages we need to hold those 
- *	structures, then we call get_bitmask_order(), which will tell us the
- *	last bit set in the number, starting with 1. (If we need 30 pages, that
- *	is 0x0000001e in hex. The last bit is the 5th, which is the order we 
- *	would use to allocate 32 contiguous pages).
- *
- *	Since we also need to save those pages, we add the number of pages that
- *	we need to nr_copy_pages, and in case of an overflow, do the 
- *	calculation again to update the number of pages needed. 
- *
- *	With this model, we will tend to waste a lot of memory if we just cross
- *	an order boundary. Plus, the higher the order of allocation that we try
- *	to do, the more likely we are to fail in a low-memory situtation 
- *	(though	we're unlikely to get this far in such a case, since swsusp 
- *	requires half of memory to be free anyway).
- */
-
-
-static void calc_order(void)
-{
-	int diff = 0;
-	int order = 0;
-
-	do {
-		diff = get_bitmask_order(SUSPEND_PD_PAGES(nr_copy_pages)) - order;
-		if (diff) {
-			order += diff;
-			nr_copy_pages += 1 << diff;
-		}
-	} while(diff);
-	pagedir_order = order;
-}
 
 
 /**
@@ -673,13 +954,14 @@
 
 static int alloc_pagedir(void)
 {
-	calc_order();
+	calc_order(&pagedir_order, &nr_copy_pages);
 	pagedir_save = (suspend_pagedir_t *)__get_free_pages(GFP_ATOMIC | __GFP_COLD,
 							     pagedir_order);
 	if (!pagedir_save)
 		return -ENOMEM;
 	memset(pagedir_save, 0, (1 << pagedir_order) * PAGE_SIZE);
 	pagedir_nosave = pagedir_save;
+	pr_debug("pagedir %p, %d\n", pagedir_save, pagedir_order);
 	return 0;
 }
 
@@ -783,7 +1065,6 @@
 int suspend_prepare_image(void)
 {
 	unsigned int nr_needed_pages = 0;
-	int error;
 
 	pr_debug("swsusp: critical section: \n");
 	if (save_highmem()) {
@@ -791,15 +1072,8 @@
 		return -ENOMEM;
 	}
 
-	drain_local_pages();
-	count_data_pages();
-	printk("swsusp: Need to copy %u pages\n",nr_copy_pages);
 	nr_needed_pages = nr_copy_pages + PAGES_FOR_IO;
 
-	error = swsusp_alloc();
-	if (error)
-		return error;
-	
 	/* During allocating of suspend pagedir, new cold pages may appear. 
 	 * Kill them.
 	 */
@@ -1011,7 +1285,7 @@
 }
 
 
-static struct block_device * resume_bdev;
+static struct block_device * resume_bdev __nosavedata;
 
 /**
  *	submit - submit BIO request.
@@ -1151,6 +1425,11 @@
 			printk( "." );
 		error = bio_read_page(swp_offset(p->swap_address),
 				  (void *)p->address);
+#ifdef PCS_DEBUG
+		pr_debug("data_read: %p %p %u\n", 
+				(void *)p->address, (void *)p->orig_address, 
+				swp_offset(p->swap_address));
+#endif
 	}
 	printk(" %d done.\n",i);
 	return error;
@@ -1219,7 +1498,7 @@
 	if (!IS_ERR(resume_bdev)) {
 		set_blocksize(resume_bdev, PAGE_SIZE);
 		error = read_suspend_image();
-		blkdev_put(resume_bdev);
+		/* blkdev_put(resume_bdev); */
 	} else
 		error = PTR_ERR(resume_bdev);
 

--
Hu Gang / Steve
Linux Registered User 204016
GPG Public Key: http://soulinfo.com/~hugang/hugang.asc

^ permalink raw reply	[flat|nested] 46+ messages in thread

end of thread, other threads:[~2004-12-23  0:55 UTC | newest]

Thread overview: 46+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2004-11-19 19:40 [PATCH] Software Suspend split to two stage V2 hugang
2004-11-20  0:15 ` Pavel Machek
2004-11-20  0:30 ` swsusp bigdiff [was Re: [PATCH] Software Suspend split to two stage V2.] Pavel Machek
2004-11-20  3:03   ` hugang
2004-11-20 10:15     ` Pavel Machek
2004-11-20  8:12   ` hugang
2004-11-20 21:22     ` Pavel Machek
2004-11-20 21:41     ` Pavel Machek
2004-11-20 22:35     ` Pavel Machek
2004-11-20 22:49     ` Pavel Machek
2004-11-21  7:48       ` hugang
2004-11-22  7:22       ` hugang
2004-11-22 10:26         ` Pavel Machek
2004-11-22 10:32           ` hugang
2004-11-22 11:02             ` Pavel Machek
2004-11-22 11:54               ` Rafael J. Wysocki
2004-11-22 21:50                 ` Nigel Cunningham
2004-11-23 21:54                   ` Pavel Machek
2004-11-23 21:57                     ` Nigel Cunningham
2004-11-24  8:03                     ` [PATH] 11-24 swsusp update 2/3 hugang
2004-11-24  8:04                     ` [PATH] 11-24 swsusp update 3/3 hugang
2004-11-24  9:13                       ` hugang
2004-11-24 14:05                       ` Colin Leroy
2004-11-22 16:58               ` [PATH] swsusp update 1/3 hugang
2004-11-23 22:14                 ` Pavel Machek
2004-11-24  8:02                   ` [PATH] 11-24 " hugang
2004-11-24 10:56                     ` Pavel Machek
2004-11-24 11:28                   ` [PATH] " Pavel Machek
2004-11-24 18:30                     ` hugang
2004-12-20 21:45                       ` Nishanth Aravamudan
2004-12-20 22:41                         ` Pavel Machek
2004-11-22 16:58               ` [PATH] swsusp update 2/3 hugang
2004-11-23 22:23                 ` Pavel Machek
2004-11-22 16:58               ` [PATH] swsusp update 3/3 hugang
2004-11-23 22:29                 ` Pavel Machek
2004-11-24 10:21                 ` Guido Guenther
2004-11-20  9:27   ` swsusp bigdiff [was Re: [PATCH] Software Suspend split to two stage V2.] hugang
2004-12-20 21:44   ` Nishanth Aravamudan
2004-12-20 22:40     ` Pavel Machek
2004-12-20 23:06     ` Zwane Mwaikambo
2004-12-20 23:28   ` Nigel Cunningham
2004-12-22 20:28     ` Pavel Machek
2004-12-22 21:21       ` Nigel Cunningham
2004-12-22 21:32         ` Pavel Machek
2004-12-23  0:52           ` Nigel Cunningham
2004-12-21 13:15   ` Paulo Marques

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox