Re: amd64-agp vs. swsusp

From: Michal Schmidt
Date: Thu Jul 21 2005 - 05:45:06 EST


Pavel Machek wrote:
Long time ago there were i386 problems because we assumed that kernel
is mapped in one big mapping and agp broke that assumption. Copying
pages backwards "fixed" it (and then we done proper fix). It should
not be, but it seems similar to this problem....

Do you mean this patch of yours?:
http://www.ussg.iu.edu/hypermail/linux/kernel/0404.3/0640.html

I'm trying to do something similar for x86_64. See the attached patch.
Unfortunately, it doesn't help. The behaviour seems unchanged (resume still works iff amd64-agp wasn't loaded before suspend).

Michal diff -Nurp -X dontdiff.new linux-mm/arch/x86_64/kernel/suspend_asm.S linux-mm.mich/arch/x86_64/kernel/suspend_asm.S
--- linux-mm/arch/x86_64/kernel/suspend_asm.S 2005-06-30 01:00:53.000000000 +0200
+++ linux-mm.mich/arch/x86_64/kernel/suspend_asm.S 2005-07-21 11:53:17.000000000 +0200
@@ -41,7 +41,7 @@ ENTRY(swsusp_arch_suspend)

ENTRY(swsusp_arch_resume)
/* set up cr3 */
- leaq init_level4_pgt(%rip),%rax
+ leaq swsusp_level4_pgt(%rip),%rax
subq $__START_KERNEL_map,%rax
movq %rax,%cr3

diff -Nurp -X dontdiff.new linux-mm/arch/x86_64/mm/init.c linux-mm.mich/arch/x86_64/mm/init.c
--- linux-mm/arch/x86_64/mm/init.c 2005-07-18 19:48:12.000000000 +0200
+++ linux-mm.mich/arch/x86_64/mm/init.c 2005-07-21 11:21:36.000000000 +0200
@@ -310,10 +310,32 @@ void __init init_memory_mapping(unsigned

extern struct x8664_pda cpu_pda[NR_CPUS];

+#ifdef CONFIG_SOFTWARE_SUSPEND
+/*
+ * Swap suspend & friends need this for resume because things like the intel-agp
+ * driver might have split up a kernel 4MB mapping.
+ */
+char __nosavedata swsusp_level4_pgt[PAGE_SIZE]
+ __attribute__ ((aligned (PAGE_SIZE)));
+
+static inline void save_pg_dir(void)
+{
+ memcpy(swsusp_level4_pgt, init_level4_pgt, PAGE_SIZE);
+}
+#else
+static inline void save_pg_dir(void)
+{
+}
+#endif
+
/* Assumes all CPUs still execute in init_mm */
void zap_low_mappings(void)
{
- pgd_t *pgd = pgd_offset_k(0UL);
+ pgd_t *pgd;
+
+ save_pg_dir();
+
+ pgd = pgd_offset_k(0UL);
pgd_clear(pgd);
flush_tlb_all();
}