All of lore.kernel.org
 help / color / mirror / Atom feed
From: Nigel Cunningham <ncunningham@clear.net.nz>
To: Pavel Machek <pavel@ucw.cz>
Cc: Linux Kernel Mailing List <linux-kernel@vger.kernel.org>
Subject: Re: SWSUSP Discontiguous pagedir patch
Date: Sat, 01 Mar 2003 17:22:53 +1300	[thread overview]
Message-ID: <1046487717.4616.22.camel@laptop-linux.cunninghams> (raw)
In-Reply-To: <20030227132024.GB27084@atrey.karlin.mff.cuni.cz>

On Fri, 2003-02-28 at 02:20, Pavel Machek wrote:
> > > b) introduces hard limit on how much pages you can save (4GB).
> > 
> > Well, I might ask how many people you know with 4GB of swap and 4GB of
> > RAM they want to suspend to disk :> Don't forget we still aren't
> > handling himem anyway (at least not last time I checked). As y
> 
> Well, on x86-64 it should be able to suspend 8GB machine just fine --
> being 64bit means you don't have to deal with himem. Plus it would
> only be 2GB limit on x86-64.
> 
[deletia]
> 
> I don't know. I'd let Linus decide. I don't like hard limit on ammount
> of mem, through.

Hi again.

I've thought things through some more. We need to keep in mind that
other patches I intend to submit save the pages that aren't needed for
the suspend process itself separately. Since this includes all the
highmem pages and a reasonable proportion of the normal pages (easily
more than half when we're talking high usage), we don't need to eat
memory and we don't really have a hard limit on the size of the image.
Presumably the same conditions will apply under x86-64.

Thus, I still think we can go with the patch I submitted before. I've
rediffed it against 2.5.63 (less the bits already applied).

Regards,

Nigel

diff -ruN linux-2.5.63/arch/i386/kernel/Makefile linux-2.5.63-01/arch/i386/kernel/Makefile
--- linux-2.5.63/arch/i386/kernel/Makefile	2003-03-01 15:10:16.000000000 +1300
+++ linux-2.5.63-01/arch/i386/kernel/Makefile	2003-03-01 15:14:28.000000000 +1300
@@ -23,7 +23,7 @@
 obj-$(CONFIG_X86_MPPARSE)	+= mpparse.o
 obj-$(CONFIG_X86_LOCAL_APIC)	+= apic.o nmi.o
 obj-$(CONFIG_X86_IO_APIC)	+= io_apic.o
-obj-$(CONFIG_SOFTWARE_SUSPEND)	+= suspend.o suspend_asm.o
+obj-$(CONFIG_SOFTWARE_SUSPEND)	+= suspend.o
 obj-$(CONFIG_X86_NUMAQ)		+= numaq.o
 obj-$(CONFIG_EDD)             	+= edd.o
 obj-$(CONFIG_MODULES)		+= module.o
diff -ruN linux-2.5.63/arch/i386/kernel/suspend.c linux-2.5.63-01/arch/i386/kernel/suspend.c
--- linux-2.5.63/arch/i386/kernel/suspend.c	2003-02-20 08:25:26.000000000 +1300
+++ linux-2.5.63-01/arch/i386/kernel/suspend.c	2003-02-20 08:27:36.000000000 +1300
@@ -133,3 +133,84 @@
 	}
 
 }
+
+/* Local variables for do_magic */
+static int loop __nosavedata = 0;
+static int loop2 __nosavedata = 0;
+
+/*
+ * FIXME: This function should really be written in assembly. Actually
+ * requirement is that it does not touch stack, because %esp will be
+ * wrong during resume before restore_processor_context(). Check
+ * assembly if you modify this.
+ */
+void do_magic(int resume)
+{
+	if (!resume) {
+		do_magic_suspend_1();
+		save_processor_state();	/* We need to capture registers and memory at "same time" */
+		asm (	"movl %esp, saved_context_esp\n\t"
+			"movl %eax, saved_context_eax\n\t"
+			"movl %ebx, saved_context_ebx\n\t"
+			"movl %ecx, saved_context_ecx\n\t"
+			"movl %edx, saved_context_edx\n\t"
+			"movl %ebp, saved_context_ebp\n\t"
+			"movl %esi, saved_context_esi\n\t"
+			"movl %edi, saved_context_edi\n\t"
+			"pushfl ; popl saved_context_eflags\n\t");
+		
+		do_magic_suspend_2();		/* If everything goes okay, this function does not return */
+		return;
+	}
+
+	/* We want to run from swapper_pg_dir, since swapper_pg_dir is stored in constant
+	 * place in memory 
+	 */
+
+        __asm__( "movl %%ecx,%%cr3\n" ::"c"(__pa(swapper_pg_dir)));
+
+/*
+ * Final function for resuming: after copying the pages to their original
+ * position, it restores the register state.
+ *
+ * What about page tables? Writing data pages may toggle
+ * accessed/dirty bits in our page tables. That should be no problems
+ * with 4MB page tables. That's why we require have_pse.  
+ *
+ * This loops destroys stack from under itself, so it better should
+ * not use any stack space, itself. When this function is entered at
+ * resume time, we move stack to _old_ place.  This is means that this
+ * function must use no stack and no local variables in registers,
+ * until calling restore_processor_context();
+ *
+ * Critical section here: noone should touch saved memory after
+ * do_magic_resume_1; copying works, because nr_copy_pages,
+ * pagedir_nosave, loop and loop2 are nosavedata.
+ */
+	do_magic_resume_1();
+
+	for (loop=0; loop < nr_copy_pages; loop++) {
+		/* You may not call something (like copy_page) here: see above */
+		for (loop2=0; loop2 < PAGE_SIZE; loop2++) {
+			*(((char *)(PAGEDIR_ENTRY(pagedir_nosave,loop)->orig_address))+loop2) =
+				*(((char *)(PAGEDIR_ENTRY(pagedir_nosave,loop)->address))+loop2);
+			__flush_tlb();
+		}
+	}
+
+	asm(	"movl saved_context_esp, %esp\n\t"
+		"movl saved_context_ebp, %ebp\n\t"
+		"movl saved_context_eax, %eax\n\t"
+		"movl saved_context_ebx, %ebx\n\t"
+		"movl saved_context_ecx, %ecx\n\t"
+		"movl saved_context_edx, %edx\n\t"
+		"movl saved_context_esi, %esi\n\t"
+		"movl saved_context_edi, %edi\n\t");
+	restore_processor_state();
+	asm("pushl saved_context_eflags ; popfl\n\t");
+
+/* Ahah, we now run with our old stack, and with registers copied from
+   suspend time */
+
+	do_magic_resume_2();
+}
diff -ruN linux-2.5.63/include/linux/page-flags.h linux-2.5.63-01/include/linux/page-flags.h
--- linux-2.5.63/include/linux/page-flags.h	2003-02-20 07:59:33.000000000 +1300
+++ linux-2.5.63-01/include/linux/page-flags.h	2003-02-20 08:28:31.000000000 +1300
@@ -74,6 +74,7 @@
 #define PG_mappedtodisk		17	/* Has blocks allocated on-disk */
 #define PG_reclaim		18	/* To be reclaimed asap */
 #define PG_compound		19	/* Part of a compound page */
+#define PG_collides		20	/* swsusp - page used in save image */
 
 /*
  * Global page accounting.  One instance per CPU.  Only unsigned longs are
@@ -256,6 +257,9 @@
 #define SetPageCompound(page)	set_bit(PG_compound, &(page)->flags)
 #define ClearPageCompound(page)	clear_bit(PG_compound, &(page)->flags)
 
+#define PageCollides(page)	test_bit(PG_collides, &(page)->flags)
+#define SetPageCollides(page)	set_bit(PG_collides, &(page)->flags)
+#define ClearPageCollides(page)	clear_bit(PG_collides, &(page)->flags)
 /*
  * The PageSwapCache predicate doesn't use a PG_flag at this time,
  * but it may again do so one day.
diff -ruN linux-2.5.63/include/linux/suspend.h linux-2.5.63-01/include/linux/suspend.h
--- linux-2.5.63/include/linux/suspend.h	2003-01-15 17:00:58.000000000 +1300
+++ linux-2.5.63-01/include/linux/suspend.h	2003-02-20 08:27:36.000000000 +1300
@@ -34,7 +34,7 @@
 	char version[20];
 	int num_cpus;
 	int page_size;
-	suspend_pagedir_t *suspend_pagedir;
+	suspend_pagedir_t **suspend_pagedir;
 	unsigned int num_pbes;
 	struct swap_location {
 		char filename[SWAP_FILENAME_MAXLENGTH];
@@ -42,6 +42,8 @@
 };
 
 #define SUSPEND_PD_PAGES(x)     (((x)*sizeof(struct pbe))/PAGE_SIZE+1)
+#define PAGEDIR_CAPACITY(x)     (((x)*PAGE_SIZE/sizeof(struct pbe)))
+#define PAGEDIR_ENTRY(pagedir, i) (pagedir[i/PAGEDIR_CAPACITY(1)] + (i%PAGEDIR_CAPACITY(1)))
    
 /* mm/vmscan.c */
 extern int shrink_mem(void);
@@ -61,7 +63,7 @@
 extern void thaw_processes(void);
 
 extern unsigned int nr_copy_pages __nosavedata;
-extern suspend_pagedir_t *pagedir_nosave __nosavedata;
+extern suspend_pagedir_t **pagedir_nosave __nosavedata;
 
 /* Communication between kernel/suspend.c and arch/i386/suspend.c */
 
diff -ruN linux-2.5.63/kernel/suspend.c linux-2.5.63-01/kernel/suspend.c
--- linux-2.5.63/kernel/suspend.c	2003-02-20 07:59:34.000000000 +1300
+++ linux-2.5.63-01/kernel/suspend.c	2003-02-20 10:42:52.000000000 +1300
@@ -96,7 +96,6 @@
 static int new_loglevel = 7;
 static int orig_loglevel = 0;
 static int orig_fgconsole, orig_kmsg;
-static int pagedir_order_check;
 static int nr_copy_pages_check;
 
 static int resume_status = 0;
@@ -116,9 +115,9 @@
    allocated at time of resume, that travels through memory not to
    collide with anything.
  */
-suspend_pagedir_t *pagedir_nosave __nosavedata = NULL;
-static suspend_pagedir_t *pagedir_save;
-static int pagedir_order __nosavedata = 0;
+suspend_pagedir_t **pagedir_nosave __nosavedata = NULL;
+static suspend_pagedir_t **pagedir_save = NULL;
+static int pagedir_size __nosavedata = 0;
 
 struct link {
 	char dummy[PAGE_SIZE - sizeof(swp_entry_t)];
@@ -395,7 +394,7 @@
 {
 	int i;
 	swp_entry_t entry, prev = { 0 };
-	int nr_pgdir_pages = SUSPEND_PD_PAGES(nr_copy_pages);
+	int pagedir_size = SUSPEND_PD_PAGES(nr_copy_pages);
 	union diskpage *cur,  *buffer = (union diskpage *)get_zeroed_page(GFP_ATOMIC);
 	unsigned long address;
 	struct page *page;
@@ -410,16 +409,15 @@
 		if (swapfile_used[swp_type(entry)] != SWAPFILE_SUSPEND)
 			panic("\nPage %d: not enough swapspace on suspend device", i );
 	    
-		address = (pagedir_nosave+i)->address;
+		address = PAGEDIR_ENTRY(pagedir_nosave,i)->address;
 		page = virt_to_page(address);
 		rw_swap_page_sync(WRITE, entry, page);
-		(pagedir_nosave+i)->swap_address = entry;
+		PAGEDIR_ENTRY(pagedir_nosave,i)->swap_address = entry;
 	}
 	printk( "|\n" );
-	printk( "Writing pagedir (%d pages): ", nr_pgdir_pages);
-	for (i=0; i<nr_pgdir_pages; i++) {
-		cur = (union diskpage *)((char *) pagedir_nosave)+i;
-		BUG_ON ((char *) cur != (((char *) pagedir_nosave) + i*PAGE_SIZE));
+	printk( "Writing pagedir (%d pages): ", pagedir_size);
+	for (i=0; i<pagedir_size; i++) {
+		cur = (union diskpage *) pagedir_nosave[i];
 		printk( "." );
 		if (!(entry = get_swap_page()).val) {
 			printk(KERN_CRIT "Not enough swapspace when writing pgdir\n" );
@@ -467,7 +465,7 @@
 }
 
 /* if pagedir_p != NULL it also copies the counted pages */
-static int count_and_copy_data_pages(struct pbe *pagedir_p)
+static int count_and_copy_data_pages(struct pbe **pagedir_p)
 {
 	int chunk_size;
 	int nr_copy_pages = 0;
@@ -507,65 +505,88 @@
 			   critical bios data? */
 		} else	BUG();
 
-		nr_copy_pages++;
 		if (pagedir_p) {
-			pagedir_p->orig_address = ADDRESS(pfn);
-			copy_page((void *) pagedir_p->address, (void *) pagedir_p->orig_address);
-			pagedir_p++;
+			PAGEDIR_ENTRY(pagedir_p, nr_copy_pages)->orig_address = ADDRESS(pfn);
+			copy_page((void *) PAGEDIR_ENTRY(pagedir_p, nr_copy_pages)->address, (void *) PAGEDIR_ENTRY(pagedir_p, nr_copy_pages)->orig_address);
 		}
+		nr_copy_pages++;
 	}
 	return nr_copy_pages;
 }
 
-static void free_suspend_pagedir(unsigned long this_pagedir)
+static void free_suspend_pagedir(struct pbe ** this_pagedir)
 {
-	struct page *page;
-	int pfn;
-	unsigned long this_pagedir_end = this_pagedir +
-		(PAGE_SIZE << pagedir_order);
+	int i;
+	int rangestart = -1, rangeend = -1;
 
-	for(pfn = 0; pfn < num_physpages; pfn++) {
-		page = pfn_to_page(pfn);
-		if (!TestClearPageNosave(page))
-			continue;
+	if (pagedir_size == 0)
+		return;
 
-		if (ADDRESS(pfn) >= this_pagedir && ADDRESS(pfn) < this_pagedir_end)
-			continue; /* old pagedir gets freed in one */
-		
-		free_page(ADDRESS(pfn));
+	for(i = 0; i < nr_copy_pages; i++) {
+		if (PAGEDIR_ENTRY(this_pagedir,i)->address) {
+			if (rangestart > -1) {
+				printk("Pagedir entry %d-%d address2 not set!\n", rangestart, rangeend);
+				rangestart = -1;
+			}
+			ClearPageNosave(virt_to_page(PAGEDIR_ENTRY(this_pagedir,i)->address));
+			free_page(PAGEDIR_ENTRY(this_pagedir,i)->address);
+		} else {
+			if (rangestart == -1)
+				rangestart = i;
+			rangeend = i;
+		}
 	}
-	free_pages(this_pagedir, pagedir_order);
+		
+	if (rangestart > -1)
+		printk("Pagedir entry %d-%d address not set!\n", rangestart, nr_copy_pages - 1);
+
+	for(i = 0; i < pagedir_size; i++)
+		free_page((unsigned long) this_pagedir[i]);
+
+	free_page((unsigned long) this_pagedir);
+	this_pagedir = NULL;
+	nr_copy_pages = 0;
+	pagedir_size = 0;
 }
 
-static suspend_pagedir_t *create_suspend_pagedir(int nr_copy_pages)
+static suspend_pagedir_t **create_suspend_pagedir(int nr_copy_pages)
 {
+	suspend_pagedir_t **pagedir;
+	struct pbe **p;
 	int i;
-	suspend_pagedir_t *pagedir;
-	struct pbe *p;
-	struct page *page;
 
-	pagedir_order = get_bitmask_order(SUSPEND_PD_PAGES(nr_copy_pages));
+	pagedir_size = SUSPEND_PD_PAGES(nr_copy_pages);
 
-	p = pagedir = (suspend_pagedir_t *)__get_free_pages(GFP_ATOMIC | __GFP_COLD, pagedir_order);
-	if(!pagedir)
+	p = pagedir = (suspend_pagedir_t **)__get_free_pages(GFP_ATOMIC | __GFP_COLD, 0);
+	if(!p)
 		return NULL;
 
-	page = virt_to_page(pagedir);
-	for(i=0; i < 1<<pagedir_order; i++)
-		SetPageNosave(page++);
-		
+	/* We aren't setting the pagedir itself Nosave because we have to be able
+	 * to free it during resume, after restoring the image. This means nr_copy_pages
+	 * needs to be adjusted */
+	
+	for (i = 0; i < pagedir_size; i++) {
+		p[i] = (suspend_pagedir_t *)__get_free_pages(GFP_ATOMIC, 0);
+		if (!p[i]) {
+			int j;
+			for (j = 0; j < i; j++) {
+				free_page((unsigned long) p[j]);
+			}
+			free_page((unsigned long) p);
+			return NULL;
+		}
+	}
+
 	while(nr_copy_pages--) {
-		p->address = get_zeroed_page(GFP_ATOMIC | __GFP_COLD);
-		if(!p->address) {
-			free_suspend_pagedir((unsigned long) pagedir);
+		PAGEDIR_ENTRY(p, nr_copy_pages)->address = get_zeroed_page(GFP_ATOMIC | __GFP_COLD);
+		if(!PAGEDIR_ENTRY(p, nr_copy_pages)->address) {
+			free_suspend_pagedir(p);
 			return NULL;
 		}
-		printk(".");
-		SetPageNosave(virt_to_page(p->address));
-		p->orig_address = 0;
-		p++;
+		SetPageNosave(virt_to_page(PAGEDIR_ENTRY(p, nr_copy_pages)->address));
+		PAGEDIR_ENTRY(p, nr_copy_pages)->orig_address = 0;
 	}
-	return pagedir;
+	return p;
 }
 
 static int prepare_suspend_console(void)
@@ -604,12 +625,13 @@
 
 static int prepare_suspend_processes(void)
 {
+	PRINTK("Syncing...\n");
+	sys_sync();
 	if (freeze_processes()) {
 		printk( KERN_ERR "Suspend failed: Not all processes stopped!\n" );
 		thaw_processes();
 		return 1;
 	}
-	sys_sync();
 	return 0;
 }
 
@@ -684,6 +706,7 @@
 	pagedir_nosave = NULL;
 	printk( "/critical section: Counting pages to copy" );
 	nr_copy_pages = count_and_copy_data_pages(NULL);
+	nr_copy_pages += 1 + SUSPEND_PD_PAGES(nr_copy_pages);
 	nr_needed_pages = nr_copy_pages + PAGES_FOR_IO;
 	
 	printk(" (pages needed: %d+%d=%d free: %d)\n",nr_copy_pages,PAGES_FOR_IO,nr_needed_pages,nr_free_pages());
@@ -713,7 +736,6 @@
 		return 1;
 	}
 	nr_copy_pages_check = nr_copy_pages;
-	pagedir_order_check = pagedir_order;
 
 	drain_local_pages();	/* During allocating of suspend pagedir, new cold pages may appear. Kill them */
 	if (nr_copy_pages != count_and_copy_data_pages(pagedir_nosave))	/* copy */
@@ -789,12 +811,11 @@
 void do_magic_resume_2(void)
 {
 	BUG_ON (nr_copy_pages_check != nr_copy_pages);
-	BUG_ON (pagedir_order_check != pagedir_order);
 
 	__flush_tlb_global();		/* Even mappings of "global" things (vmalloc) need to be fixed */
 
 	PRINTK( "Freeing prev allocated pagedir\n" );
-	free_suspend_pagedir((unsigned long) pagedir_save);
+	free_suspend_pagedir(pagedir_save);
 	spin_unlock_irq(&suspend_pagedir_lock);
 	drivers_resume(RESUME_ALL_PHASES);
 
@@ -831,7 +852,7 @@
 	spin_lock_irq(&suspend_pagedir_lock);	/* Done to disable interrupts */ 
 	mdelay(1000);
 
-	free_pages((unsigned long) pagedir_nosave, pagedir_order);
+	free_suspend_pagedir(pagedir_nosave);
 	spin_unlock_irq(&suspend_pagedir_lock);
 	mark_swapfiles(((swp_entry_t) {0}), MARK_SWAP_RESUME);
 	PRINTK(KERN_WARNING "%sLeaving do_magic_suspend_2...\n", name_suspend);	
@@ -894,37 +915,23 @@
 
 /* More restore stuff */
 
-/* FIXME: Why not memcpy(to, from, 1<<pagedir_order*PAGE_SIZE)? */
-static void copy_pagedir(suspend_pagedir_t *to, suspend_pagedir_t *from)
-{
-	int i;
-	char *topointer=(char *)to, *frompointer=(char *)from;
-
-	for(i=0; i < 1 << pagedir_order; i++) {
-		copy_page(topointer, frompointer);
-		topointer += PAGE_SIZE;
-		frompointer += PAGE_SIZE;
-	}
-}
-
-#define does_collide(addr) does_collide_order(pagedir_nosave, addr, 0)
-
-/*
- * Returns true if given address/order collides with any orig_address 
- */
-static int does_collide_order(suspend_pagedir_t *pagedir, unsigned long addr,
-		int order)
-{
+static void warmup_collision_cache(suspend_pagedir_t **pagedir) {
 	int i;
-	unsigned long addre = addr + (PAGE_SIZE<<order);
 	
-	for(i=0; i < nr_copy_pages; i++)
-		if((pagedir+i)->orig_address >= addr &&
-			(pagedir+i)->orig_address < addre)
-			return 1;
+	PRINTK("Setting up pagedir cache");
+	for (i = 0; i < max_pfn; i++)
+		ClearPageCollides(pfn_to_page(i));
 
-	return 0;
+	for(i=0; i < nr_copy_pages; i++) {
+		SetPageCollides(virt_to_page(PAGEDIR_ENTRY(pagedir, i)->orig_address));
+		if (!(i%800)) {
+			PRINTK(".");
+		}
+	}
+	PRINTK("%d", i);
+	PRINTK("|\n");
 }
+#define does_collide(address) (PageCollides(virt_to_page(address)))
 
 /*
  * We check here that pagedir & pages it points to won't collide with pages
@@ -932,64 +939,106 @@
  */
 static int check_pagedir(void)
 {
-	int i;
+	int i, nrdone = 0;
+	void **eaten_memory = NULL;
+	void **c = eaten_memory, *f, *addr;
 
 	for(i=0; i < nr_copy_pages; i++) {
-		unsigned long addr;
-
-		do {
-			addr = get_zeroed_page(GFP_ATOMIC);
-			if(!addr)
-				return -ENOMEM;
-		} while (does_collide(addr));
-
-		(pagedir_nosave+i)->address = addr;
+		while ((addr = (void *) get_zeroed_page(GFP_ATOMIC))) {
+			memset(addr, 0, PAGE_SIZE);
+			if (!does_collide((unsigned long) addr)) {
+				break;
+			}
+			eaten_memory = addr;
+			*eaten_memory = c;
+			c = eaten_memory;
+		}
+		PAGEDIR_ENTRY(pagedir_nosave,i)->address = (unsigned long) addr;
+		nrdone++;
+	}
+	
+	// Free unwanted memory
+	c = eaten_memory;
+	while(c) {
+		f = c;
+		c = *c;
+		if (f)
+			free_page((unsigned long) f);
 	}
+	eaten_memory = NULL;
+	
 	return 0;
 }
 
 static int relocate_pagedir(void)
 {
+	void **eaten_memory = NULL;
+	void **c = eaten_memory, *m = NULL, *f;
+	int oom = 0, i, numeaten = 0;
+	int pagedir_size = SUSPEND_PD_PAGES(nr_copy_pages);
+
 	/*
 	 * We have to avoid recursion (not to overflow kernel stack),
 	 * and that's why code looks pretty cryptic 
 	 */
-	suspend_pagedir_t *new_pagedir, *old_pagedir = pagedir_nosave;
-	void **eaten_memory = NULL;
-	void **c = eaten_memory, *m, *f;
-
-	printk("Relocating pagedir");
 
-	if(!does_collide_order(old_pagedir, (unsigned long)old_pagedir, pagedir_order)) {
-		printk("not neccessary\n");
-		return 0;
-	}
+	PRINTK("Relocating conflicting parts of pagedir.\n");
 
-	while ((m = (void *) __get_free_pages(GFP_ATOMIC, pagedir_order))) {
-		memset(m, 0, PAGE_SIZE);
-		if (!does_collide_order(old_pagedir, (unsigned long)m, pagedir_order))
-			break;
-		eaten_memory = m;
-		printk( "." ); 
-		*eaten_memory = c;
-		c = eaten_memory;
-	}
+	for (i = -1; i < pagedir_size; i++) {
+		int this_collides = 0;
 
-	if (!m)
-		return -ENOMEM;
-
-	pagedir_nosave = new_pagedir = m;
-	copy_pagedir(new_pagedir, old_pagedir);
+		if (i == -1)
+			this_collides = does_collide((unsigned long) pagedir_nosave);
+		else
+			this_collides = does_collide((unsigned long) pagedir_nosave[i]);
+		
+		if (this_collides) {
+			while ((m = (void *) __get_free_pages(GFP_ATOMIC, 0))) {
+				memset(m, 0, PAGE_SIZE);
+				if (!does_collide((unsigned long)m)) {
+					if (i == -1) {
+						copy_page(m, pagedir_nosave);
+						free_page((unsigned long) pagedir_nosave);
+						pagedir_nosave = m;
+					}
+					else {
+						copy_page(m, (void *) pagedir_nosave[i]);
+						free_page((unsigned long) pagedir_nosave[i]);
+						pagedir_nosave[i] = m;
+					}
+					break;
+				}
+				numeaten++;
+				eaten_memory = m;
+				PRINTK("Eaten: %d. Still to try:%d\r", numeaten, nr_free_pages()); 
+				*eaten_memory = c;
+				c = eaten_memory;
+			}
+			if (!m) {
+				printk("\nRan out of memory trying to relocate pagedir (tried %d pages).\n", numeaten);
+				oom = 1;
+				break;
+			}
+		}
 
+	}
+		
+	PRINTK("\nFreeing rejected memory locations...");
 	c = eaten_memory;
 	while(c) {
-		printk(":");
-		f = *c;
+		f = c;
 		c = *c;
 		if (f)
-			free_pages((unsigned long)f, pagedir_order);
+			free_pages((unsigned long) f, 0);
 	}
-	printk("|\n");
+	eaten_memory = NULL;
+	
+	PRINTK("\n");
+	
+	if (oom) 
+		return -ENOMEM;
+	else
+		return 0;
 	return 0;
 }
 
@@ -1062,7 +1111,7 @@
 static int __read_suspend_image(struct block_device *bdev, union diskpage *cur, int noresume)
 {
 	swp_entry_t next;
-	int i, nr_pgdir_pages;
+	int i, pagedir_size;
 
 #define PREPARENEXT \
 	{	next = cur->link.next; \
@@ -1110,24 +1159,39 @@
 
 	pagedir_save = cur->sh.suspend_pagedir;
 	nr_copy_pages = cur->sh.num_pbes;
-	nr_pgdir_pages = SUSPEND_PD_PAGES(nr_copy_pages);
-	pagedir_order = get_bitmask_order(nr_pgdir_pages);
+	pagedir_size = SUSPEND_PD_PAGES(nr_copy_pages);
 
-	pagedir_nosave = (suspend_pagedir_t *)__get_free_pages(GFP_ATOMIC, pagedir_order);
+	pagedir_nosave = (suspend_pagedir_t **)__get_free_pages(GFP_ATOMIC, 0);
 	if (!pagedir_nosave)
 		return -ENOMEM;
 
+	{
+		int i;
+		for (i = 0; i < pagedir_size; i++) {
+			pagedir_nosave[i] = (suspend_pagedir_t *)__get_free_pages(GFP_ATOMIC, 0);
+			if (!pagedir_nosave[i]) {
+				int j;
+				for (j = 0; j < i; j++)
+					free_page((unsigned long) pagedir_nosave[j]);
+				free_page((unsigned long) pagedir_nosave);
+				spin_unlock_irq(&suspend_pagedir_lock);
+				return -ENOMEM;
+			}
+		}
+	}
 	PRINTK( "%sReading pagedir, ", name_resume );
 
 	/* We get pages in reverse order of saving! */
-	for (i=nr_pgdir_pages-1; i>=0; i--) {
+	for (i=pagedir_size-1; i>=0; i--) {
 		BUG_ON (!next.val);
-		cur = (union diskpage *)((char *) pagedir_nosave)+i;
+		cur = (union diskpage *) pagedir_nosave[i];
 		if (bdev_read_page(bdev, next.val, cur)) return -EIO;
 		PREPARENEXT;
 	}
 	BUG_ON (next.val);
 
+ 	warmup_collision_cache(pagedir_nosave);
+ 
 	if (relocate_pagedir())
 		return -ENOMEM;
 	if (check_pagedir())
@@ -1135,12 +1199,12 @@
 
 	printk( "Reading image data (%d pages): ", nr_copy_pages );
 	for(i=0; i < nr_copy_pages; i++) {
-		swp_entry_t swap_address = (pagedir_nosave+i)->swap_address;
+		swp_entry_t swap_address = PAGEDIR_ENTRY(pagedir_nosave,i)->swap_address;
 		if (!(i%100))
 			printk( "." );
 		/* You do not need to check for overlaps...
 		   ... check_pagedir already did this work */
-		if (bdev_read_page(bdev, swp_offset(swap_address) * PAGE_SIZE, (char *)((pagedir_nosave+i)->address)))
+		if (bdev_read_page(bdev, swp_offset(swap_address) * PAGE_SIZE, (char *)(PAGEDIR_ENTRY(pagedir_nosave,i)->address)))
 			return -EIO;
 	}
 	printk( "|\n" );




  parent reply	other threads:[~2003-03-01  4:10 UTC|newest]

Thread overview: 41+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <1045784829.3821.10.camel@laptop-linux.cunninghams>
     [not found] ` <20030223223757.GA120@elf.ucw.cz>
     [not found]   ` <1046136752.1784.15.camel@laptop-linux.cunninghams>
     [not found]     ` <20030227132024.GB27084@atrey.karlin.mff.cuni.cz>
2003-02-27 18:42       ` SWSUSP Discontiguous pagedirs Nigel Cunningham
2003-03-01  4:22       ` Nigel Cunningham [this message]
2003-03-02 23:55         ` SWSUSP Discontiguous pagedir patch Patrick Mochel
2003-03-03  2:06           ` Nigel Cunningham
2003-03-03  2:31           ` Nigel Cunningham
2003-03-03 12:30           ` Pavel Machek
2003-03-04 20:36             ` Patrick Mochel
2003-03-05 20:50               ` Pavel Machek
2003-03-05 21:52                 ` Linux vs Windows temperature anomaly Jonathan Lundell
2003-03-05 23:11                   ` Herman Oosthuysen
2003-03-05 23:38                     ` Con Kolivas
2003-03-05 23:50                       ` Russell King
2003-03-06  0:29                         ` Ed Sweetman
2003-03-06  0:47                           ` Trever L. Adams
2003-03-06  9:45                             ` Russell King
2003-03-06  1:58                           ` Jonathan Lundell
2003-03-06  7:18                       ` Corvus Corax
2003-03-06  7:57                         ` Ed Sweetman
2003-03-06  8:18                           ` Corvus Corax
2003-03-06  8:58                             ` Ed Sweetman
2003-03-06 15:41                               ` Jesse Pollard
2003-03-06 14:27                         ` Jesse Pollard
2003-03-06  2:57                   ` David Rees
2003-03-06  6:12                   ` Matthias Schniedermeyer
2003-03-06 16:07                     ` Jonathan Lundell
2003-03-07  0:40                   ` Horst von Brand
2003-03-05 18:02           ` SWSUSP Discontiguous pagedir patch Pavel Machek
2003-03-07 17:14             ` Patrick Mochel
2003-03-07 20:27               ` Pavel Machek
2003-03-09 19:39                 ` Benjamin Herrenschmidt
2003-03-09 20:12                   ` Pavel Machek
2003-03-10 16:49                 ` Patrick Mochel
2003-03-10 19:23                   ` Pavel Machek
2003-03-10 19:05                     ` Patrick Mochel
2003-03-10 22:17                     ` Nigel Cunningham
2003-03-10 23:20                       ` Pavel Machek
2003-03-07 20:36               ` Pavel Machek
2003-03-10 16:51                 ` Patrick Mochel
2003-03-10 19:12                   ` Pavel Machek
2003-03-10 18:59                     ` Patrick Mochel
2003-03-07 20:41               ` Pavel Machek

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1046487717.4616.22.camel@laptop-linux.cunninghams \
    --to=ncunningham@clear.net.nz \
    --cc=linux-kernel@vger.kernel.org \
    --cc=pavel@ucw.cz \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.