diff -ruN linux-2.4.24/arch/arm/kernel/ecard.c software-suspend-linux-2.4.24-rev7/arch/arm/kernel/ecard.c --- linux-2.4.24/arch/arm/kernel/ecard.c 2004-01-22 19:49:42.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/arch/arm/kernel/ecard.c 2004-01-30 15:23:38.000000000 +1300 @@ -298,6 +298,7 @@ ecard_task(void * unused) { struct task_struct *tsk = current; + DECLARE_SWSUSP_LOCAL_VAR; /* * We don't want /any/ signals, not even SIGKILL @@ -308,6 +309,9 @@ strcpy(tsk->comm, "kecardd"); daemonize(); + SWSUSP_THREAD_FLAGS_RESET; + SWSUSP_ACTIVITY_START(PF_SYNCTHREAD); + /* * Allocate a mm. We're not a lazy-TLB kernel task since we need * to set page table entries where the user space would be. Note @@ -325,7 +329,9 @@ if (req == NULL) { sigemptyset(&tsk->pending.signal); + SWSUSP_ACTIVITY_PAUSING; interruptible_sleep_on(&ecard_wait); + SWSUSP_ACTIVITY_RESTARTING(PF_SYNCTHREAD); } } while (req == NULL); diff -ruN linux-2.4.24/arch/i386/config.in software-suspend-linux-2.4.24-rev7/arch/i386/config.in --- linux-2.4.24/arch/i386/config.in 2004-01-22 19:46:20.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/arch/i386/config.in 2004-01-30 15:23:38.000000000 +1300 @@ -331,6 +331,8 @@ bool 'Power Management support' CONFIG_PM +source kernel/power/Config.in + dep_tristate ' Advanced Power Management BIOS support' CONFIG_APM $CONFIG_PM if [ "$CONFIG_APM" != "n" ]; then bool ' Ignore USER SUSPEND' CONFIG_APM_IGNORE_USER_SUSPEND diff -ruN linux-2.4.24/arch/i386/defconfig software-suspend-linux-2.4.24-rev7/arch/i386/defconfig --- linux-2.4.24/arch/i386/defconfig 2004-01-22 19:45:54.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/arch/i386/defconfig 2004-01-30 15:23:38.000000000 +1300 @@ -111,6 +111,7 @@ CONFIG_BINFMT_MISC=y CONFIG_PM=y # CONFIG_APM is not set +# CONFIG_SOFTWARE_SUSPEND2 is not set # # Memory Technology Devices (MTD) diff -ruN linux-2.4.24/arch/i386/kernel/apic.c software-suspend-linux-2.4.24-rev7/arch/i386/kernel/apic.c --- linux-2.4.24/arch/i386/kernel/apic.c 2004-01-22 19:51:05.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/arch/i386/kernel/apic.c 2004-01-30 15:23:38.000000000 +1300 @@ -10,6 +10,7 @@ * for testing these extensively. * Maciej W. Rozycki : Various updates and fixes. * Mikael Pettersson : Power Management for UP-APIC. + * Nigel Cunningham : APIC support for Software Suspend. */ #include @@ -131,6 +132,29 @@ } } +/* For Software Suspend */ +void reenable_local_APIC(void) +{ + unsigned long value; + + clear_local_APIC(); + + /* + * Disable APIC (implies clearing of registers + * for 82489DX!). + */ + value = apic_read(APIC_SPIV); + value |= APIC_SPIV_APIC_ENABLED; + apic_write_around(APIC_SPIV, value); + + if (enabled_via_apicbase) { + unsigned int l, h; + rdmsr(MSR_IA32_APICBASE, l, h); + l |= MSR_IA32_APICBASE_ENABLE; + wrmsr(MSR_IA32_APICBASE, l, h); + } +} + void disable_local_APIC(void) { unsigned long value; @@ -448,7 +472,7 @@ #include #include -static struct { +static struct apic_save_data { /* 'active' is true if the local APIC was enabled by us and not the BIOS; this signifies that we are also responsible for disabling it before entering apm/acpi suspend */ @@ -1221,3 +1245,57 @@ return 0; } + +#ifdef CONFIG_SOFTWARE_SUSPEND2 +static struct apic_save_data apic_saved_states[NR_CPUS]; + +void swsusp_apic_save_state(void) +{ + struct apic_save_data * apic_data = &apic_saved_states[smp_processor_id()]; + + apic_data->apic_id = apic_read(APIC_ID); + apic_data->apic_taskpri = apic_read(APIC_TASKPRI); + apic_data->apic_ldr = apic_read(APIC_LDR); + apic_data->apic_dfr = apic_read(APIC_DFR); + apic_data->apic_spiv = apic_read(APIC_SPIV); + apic_data->apic_lvtt = apic_read(APIC_LVTT); + apic_data->apic_lvtpc = apic_read(APIC_LVTPC); + apic_data->apic_lvt0 = apic_read(APIC_LVT0); + apic_data->apic_lvt1 = apic_read(APIC_LVT1); + apic_data->apic_lvterr = apic_read(APIC_LVTERR); + apic_data->apic_tmict = apic_read(APIC_TMICT); + apic_data->apic_tdcr = apic_read(APIC_TDCR); +} + +void swsusp_apic_reload_state(void) +{ + unsigned int l, h; + struct apic_save_data * apic_data = &apic_saved_states[smp_processor_id()]; + + /* + * Make sure the APICBASE points to the right address + */ + rdmsr(MSR_IA32_APICBASE, l, h); + l &= ~MSR_IA32_APICBASE_BASE; + l |= MSR_IA32_APICBASE_ENABLE | mp_lapic_addr; + wrmsr(MSR_IA32_APICBASE, l, h); + + apic_write(APIC_LVTERR, ERROR_APIC_VECTOR | APIC_LVT_MASKED); + apic_write(APIC_ID, apic_data->apic_id); + apic_write(APIC_DFR, apic_data->apic_dfr); + apic_write(APIC_LDR, apic_data->apic_ldr); + apic_write(APIC_TASKPRI, apic_data->apic_taskpri); + apic_write(APIC_SPIV, apic_data->apic_spiv); + apic_write(APIC_LVT0, apic_data->apic_lvt0); + apic_write(APIC_LVT1, apic_data->apic_lvt1); + apic_write(APIC_LVTPC, apic_data->apic_lvtpc); + apic_write(APIC_LVTT, apic_data->apic_lvtt); + apic_write(APIC_TDCR, apic_data->apic_tdcr); + apic_write(APIC_TMICT, apic_data->apic_tmict); + apic_write(APIC_ESR, 0); + apic_read(APIC_ESR); + apic_write(APIC_LVTERR, apic_data->apic_lvterr); + apic_write(APIC_ESR, 0); + apic_read(APIC_ESR); +} +#endif diff -ruN linux-2.4.24/arch/i386/kernel/apm.c software-suspend-linux-2.4.24-rev7/arch/i386/kernel/apm.c --- linux-2.4.24/arch/i386/kernel/apm.c 2004-01-22 19:45:45.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/arch/i386/kernel/apm.c 2004-01-30 15:23:38.000000000 +1300 @@ -215,6 +215,7 @@ #include #include #include +#include #include #include @@ -1419,9 +1420,12 @@ static void apm_mainloop(void) { DECLARE_WAITQUEUE(wait, current); + DECLARE_SWSUSP_LOCAL_VAR; add_wait_queue(&apm_waitqueue, &wait); set_current_state(TASK_INTERRUPTIBLE); + SWSUSP_THREAD_FLAGS_RESET; + for (;;) { schedule_timeout(APM_CHECK_TIMEOUT); if (exit_kapmd) @@ -1431,7 +1435,9 @@ * so as not to count towards the load average).. */ set_current_state(TASK_INTERRUPTIBLE); + SWSUSP_ACTIVITY_START(PF_SYNCTHREAD); apm_event_handler(); + SWSUSP_ACTIVITY_END; } remove_wait_queue(&apm_waitqueue, &wait); } @@ -1710,6 +1716,7 @@ daemonize(); strcpy(current->comm, "kapmd"); + current->flags |= PF_NOFREEZE; sigfillset(¤t->blocked); #ifdef CONFIG_SMP diff -ruN linux-2.4.24/arch/i386/kernel/irq.c software-suspend-linux-2.4.24-rev7/arch/i386/kernel/irq.c --- linux-2.4.24/arch/i386/kernel/irq.c 2004-01-22 19:45:48.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/arch/i386/kernel/irq.c 2004-01-30 15:23:38.000000000 +1300 @@ -1091,7 +1091,7 @@ static struct proc_dir_entry * smp_affinity_entry [NR_IRQS]; -static unsigned long irq_affinity [NR_IRQS] = { [0 ... NR_IRQS-1] = ~0UL }; +unsigned long irq_affinity [NR_IRQS] = { [0 ... NR_IRQS-1] = ~0UL }; static int irq_affinity_read_proc (char *page, char **start, off_t off, int count, int *eof, void *data) { diff -ruN linux-2.4.24/arch/i386/kernel/mtrr.c software-suspend-linux-2.4.24-rev7/arch/i386/kernel/mtrr.c --- linux-2.4.24/arch/i386/kernel/mtrr.c 2004-01-22 19:45:44.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/arch/i386/kernel/mtrr.c 2004-01-30 15:23:38.000000000 +1300 @@ -245,6 +245,10 @@ 20010327 Dave Jones Adapted Cyrix III support to include VIA C3. + v1.41 + 20040107 + Nigel Cunningham + Made Software Suspend's MTRR support into a PM handler. */ #include #include @@ -270,6 +274,9 @@ #include #include #include +#ifdef CONFIG_PM +#include +#endif #include #include @@ -284,7 +291,7 @@ #include #include -#define MTRR_VERSION "1.40 (20010327)" +#define MTRR_VERSION "1.41 (20040107)" #define TRUE 1 #define FALSE 0 @@ -2275,6 +2282,93 @@ } /* End Function mtrr_init_secondary_cpu */ #endif /* CONFIG_SMP */ +#ifdef CONFIG_PM +struct mtrr_suspend_state +{ + mtrr_type ltype; + unsigned long lbase, lsize; +}; + +static struct mtrr_suspend_state *mtrr_suspend_buffers = NULL; +static int buffer_size = 0; + +static void mtrr_suspend(void) +{ + int i, max; + static struct mtrr_suspend_state * current_buffer; + + max = get_num_var_ranges(); + + if (!mtrr_suspend_buffers) { + /* Attempt to allocate new storage */ + + buffer_size = max * sizeof(struct mtrr_suspend_state); + + mtrr_suspend_buffers = kmalloc(buffer_size, GFP_KERNEL); + + if (!mtrr_suspend_buffers) { + printk("mtrr suspend: Failed to kmalloc %d bytes for mtrr suspend buffers.\n", + buffer_size); + return; + } + } + + current_buffer = mtrr_suspend_buffers; + + for (i = 0; i < max; ++i,current_buffer++) + (*get_mtrr) (i, + &(current_buffer->lbase), + &(current_buffer->lsize), + &(current_buffer->ltype)); + +} + +/* We restore mtrrs from buffer ptr */ +static void mtrr_resume(void) +{ + int i, max, len; + struct mtrr_suspend_state *current_buffer; + + + if (!mtrr_suspend_buffers) + return; + + max = get_num_var_ranges(); + len = max * sizeof(struct mtrr_suspend_state); + if(buffer_size != len) { + printk ("mtrr_resume: Resuming failed due to different number of MTRRs\n"); + kfree(mtrr_suspend_buffers); + mtrr_suspend_buffers = NULL; + return; + } + + current_buffer = mtrr_suspend_buffers; + + for (i = 0; i < max; ++i,current_buffer++) + if (current_buffer->lsize) + set_mtrr(i, + current_buffer->lbase, + current_buffer->lsize, + current_buffer->ltype); + + kfree(mtrr_suspend_buffers); + mtrr_suspend_buffers = NULL; + return; +} + +static int mtrr_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data) +{ + switch (rqst) { + case PM_SUSPEND: + mtrr_suspend(); + break; + case PM_RESUME: + mtrr_resume(); + } + return 0; +} +#endif + int __init mtrr_init(void) { #ifdef CONFIG_SMP @@ -2301,10 +2395,15 @@ S_IFREG | S_IRUGO | S_IWUSR, &mtrr_fops, NULL); #endif +#ifdef CONFIG_PM + if (!pm_register(PM_SYS_DEV, 0, mtrr_pm_callback)) + panic("Couldn't register with PM subsystem\n"); +#endif init_table (); return 0; } /* End Function mtrr_init */ + /* * Local Variables: * mode:c diff -ruN linux-2.4.24/arch/i386/kernel/process.c software-suspend-linux-2.4.24-rev7/arch/i386/kernel/process.c --- linux-2.4.24/arch/i386/kernel/process.c 2004-01-22 19:45:34.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/arch/i386/kernel/process.c 2004-01-30 15:23:38.000000000 +1300 @@ -752,19 +752,30 @@ asmlinkage int sys_fork(struct pt_regs regs) { - return do_fork(SIGCHLD, regs.esp, ®s, 0); + int result; + DECLARE_SWSUSP_LOCAL_VAR; + SWSUSP_ACTIVITY_START(0); + result = do_fork(SIGCHLD, regs.esp, ®s, 0); + SWSUSP_ACTIVITY_END; + return result; } asmlinkage int sys_clone(struct pt_regs regs) { unsigned long clone_flags; unsigned long newsp; + int result; + DECLARE_SWSUSP_LOCAL_VAR; + + SWSUSP_ACTIVITY_START(0); clone_flags = regs.ebx; newsp = regs.ecx; if (!newsp) newsp = regs.esp; - return do_fork(clone_flags, newsp, ®s, 0); + result = do_fork(clone_flags, newsp, ®s, 0); + SWSUSP_ACTIVITY_END; + return result; } /* @@ -779,7 +790,14 @@ */ asmlinkage int sys_vfork(struct pt_regs regs) { - return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.esp, ®s, 0); + int result; + + DECLARE_SWSUSP_LOCAL_VAR; + + SWSUSP_ACTIVITY_START(0); + result = do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.esp, ®s, 0); + SWSUSP_ACTIVITY_END; + return result; } /* @@ -789,7 +807,9 @@ { int error; char * filename; + DECLARE_SWSUSP_LOCAL_VAR; + SWSUSP_ACTIVITY_START(0); filename = getname((char *) regs.ebx); error = PTR_ERR(filename); if (IS_ERR(filename)) @@ -799,6 +819,7 @@ current->ptrace &= ~PT_DTRACE; putname(filename); out: + SWSUSP_ACTIVITY_END; return error; } diff -ruN linux-2.4.24/arch/i386/kernel/signal.c software-suspend-linux-2.4.24-rev7/arch/i386/kernel/signal.c --- linux-2.4.24/arch/i386/kernel/signal.c 2004-01-22 19:47:27.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/arch/i386/kernel/signal.c 2004-01-30 15:23:38.000000000 +1300 @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -595,6 +596,12 @@ if ((regs->xcs & 3) != 3) return 1; + if (current->flags & PF_FREEZE) { + refrigerator(PF_FREEZE); + if (!signal_pending(current)) + goto no_signal; + } + if (!oldset) oldset = ¤t->blocked; @@ -702,6 +709,7 @@ return 1; } + no_signal: /* Did we come from a system call? */ if (regs->orig_eax >= 0) { /* Restart the system call - no handlers present */ diff -ruN linux-2.4.24/arch/i386/kernel/smp.c software-suspend-linux-2.4.24-rev7/arch/i386/kernel/smp.c --- linux-2.4.24/arch/i386/kernel/smp.c 2004-01-22 19:45:49.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/arch/i386/kernel/smp.c 2004-01-30 15:23:38.000000000 +1300 @@ -470,7 +470,7 @@ flush_tlb_others(cpu_mask, mm, va); } -static inline void do_flush_tlb_all_local(void) +inline void do_flush_tlb_all_local(void) { unsigned long cpu = smp_processor_id(); diff -ruN linux-2.4.24/arch/i386/mm/pageattr.c software-suspend-linux-2.4.24-rev7/arch/i386/mm/pageattr.c --- linux-2.4.24/arch/i386/mm/pageattr.c 2004-01-22 19:46:20.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/arch/i386/mm/pageattr.c 2004-01-30 15:23:38.000000000 +1300 @@ -15,7 +15,8 @@ #define LARGE_PAGE_MASK (~(LARGE_PAGE_SIZE-1)) #define LARGE_PAGE_SIZE (1UL << PMD_SHIFT) -static inline pte_t *lookup_address(unsigned long address) +// Also used in kernel/suspend/low_level_io.c +inline pte_t *lookup_address(unsigned long address) { pmd_t *pmd; pgd_t *pgd = pgd_offset(&init_mm, address); diff -ruN linux-2.4.24/arch/i386/vmlinux.lds software-suspend-linux-2.4.24-rev7/arch/i386/vmlinux.lds --- linux-2.4.24/arch/i386/vmlinux.lds 2004-01-22 19:46:03.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/arch/i386/vmlinux.lds 2004-01-30 15:23:38.000000000 +1300 @@ -53,6 +53,12 @@ __init_end = .; . = ALIGN(4096); + __nosave_begin = .; + .data_nosave : { *(.data.nosave) } + . = ALIGN(4096); + __nosave_end = .; + + . = ALIGN(4096); .data.page_aligned : { *(.data.idt) } . = ALIGN(32); diff -ruN linux-2.4.24/CREDITS software-suspend-linux-2.4.24-rev7/CREDITS --- linux-2.4.24/CREDITS 2004-01-22 19:45:58.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/CREDITS 2004-01-30 15:23:38.000000000 +1300 @@ -501,6 +501,14 @@ S: Fremont, California 94539 S: USA +N: Florent Chabaud +E: florent.chabaud@polytechnique.org +D: software suspend +S: SGDN/DCSSI/SDS/LTI +S: 58, Bd Latour-Maubourg +S: 75700 Paris 07 SP +S: France + N: Gordon Chaffee E: chaffee@cs.berkeley.edu W: http://bmrc.berkeley.edu/people/chaffee/ @@ -631,6 +639,12 @@ S: NN1 3QT S: United Kingdom +N: Nigel Cunningham +E: ncunningham@users.sourceforge.net +L: http://lists.sourceforge.net/lists/listinfo/swsusp-devel +D: Major Software Suspend enchancements +S: Hastings, New Zealand + N: Stephane Dalton E: sdalton@videotron.ca D: Tieman Voyager USB Braille display driver. @@ -988,6 +1002,13 @@ S: San Jose, California 95131 S: USA +N: Nathan Friess +E: natmanz@shaw.ca +D: software suspend +S: 25 Tararidge Close NE +S: Calgary, Alberta T3J 2P4 +S: Canada + N: Fernando Fuganti E: fuganti@conectiva.com.br E: fuganti@netbank.com.br @@ -1738,6 +1759,11 @@ S: D-91080 Uttenreuth S: Germany +N: Gabor Kuti +E: seasons@falcon.sch.bme.hu +E: seasons@makosteszta.sote.hu +D: software suspend + N: Jaroslav Kysela E: perex@suse.cz W: http://www.perex.cz diff -ruN linux-2.4.24/Documentation/Configure.help software-suspend-linux-2.4.24-rev7/Documentation/Configure.help --- linux-2.4.24/Documentation/Configure.help 2004-01-22 19:50:27.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/Documentation/Configure.help 2004-01-30 15:23:38.000000000 +1300 @@ -109,6 +109,93 @@ Unless you know what you are doing you *should not* enable this option. +Software Suspend +CONFIG_SOFTWARE_SUSPEND2 + Enable the possibilty of suspending your machine to disk. No special + hardware support (BIOS, APM or ACPI) is required, but your mileage may + vary because support for hardware is currently limited. This is partly + due to the lack of a driver model in 2.4 and partly due to the fact + that it has been developed for x86+ide in the first instance. + + Please read Documentation/swsusp.txt for more information. + +Debugging info for Software Suspend +CONFIG_SOFTWARE_SUSPEND_DEBUG + This option enables the inclusion of debugging info in the software + suspend code. Turning it off will reduce the kernel size but make + debugging suspend & resume issues harder to do. + + For normal usage, this option can be turned off. + +GZIP-Compress the Software Suspend image +CONFIG_SOFTWARE_SUSPEND_GZIP_COMPRESSION + This option enables compression of pages stored during the Software Suspend + process. Pages are compressed using the zlib library in its fastest mode. + If your swap device is significantly slower than your CPU, you may improve + the speed of a suspend/resume cycle by enabling this option. + + You may also benefit from it if your swap space is small. Note, however, + that since we can't know how big the image will be until we actually + compress it, the algorithm assumes by default that no compression will be + achieved and ensures that your data will fit on disk even if that happens. + You can change this behaviour using the expected_compression parameter. + If, however, the expected ratio is not acheived, the suspend will fail and + your computer will resume. You can see the ratio achieved in a cycle by + examining dmesg or /var/log/messages after a cycle. + + This option should be off for most people. + +LZF-Compress the Software Suspend image +CONFIG_SOFTWARE_SUSPEND_LZF_COMPRESSION + Suspend process. Pages are compressed using liblzf, which is a very + fast algorithm that should essentially be free to use on any "modern" + cpu. Which is good, since it does not compress so well. + + You may also benefit from it if your swap space is small. Note, however, + that since we can't know how big the image will be until we actually + compress it, the algorithm assumes by default that no compression will be + achieved and ensures that your data will fit on disk even if that happens. + You can change this behaviour using the expected_compression parameter. + If, however, the expected ratio is not acheived, the suspend will fail and + your computer will resume. You can see the ratio achieved in a cycle by + examining dmesg or /var/log/messages after a cycle. + + This option should be off for most people, as it is experimental. + +Support for writing image to swap +CONFIG_SOFTWARE_SUSPEND_SWAPWRITER + This option enables support for writing your image to a swap partition + or swapfile. + + Most people will want to say Yes here. + +Keep and reuse Software Suspend images. +CONFIG_SOFTWARE_SUSPEND_KEEP_IMAGE + After resuming, Software Suspend normally frees the swap space it + used for storing your image and resets the swap signature to its + normal state. This stops a future boot from seeing the image and + loading it again, possibly resulting in corruption of your file + system. + + If, however, you are running entirely from read-only filesystems, + it doesn't matter if you boot from the same image a number of times, + as nothing will have changed and there will be no possibility of + corruption. In this case, you might want to enable this option and + read Documentation/swsusp.txt for details on how to use it. + + Since usage of this option also requires explicitly setting a + /proc/swsusp entry, it is safe to say 'Y' here. Most users will however + never use or want this option, and should therefore say 'N' to get an + extra degree of protection against accidentally invoking the functionality. + +Relaxed permissions on /proc/swsusp entries. +CONFIG_SOFTWARE_SUSPEND_RELAXED_PROC + This option makes /proc/swsusp entries world-accessible, rather than + root-only. It is intended for systems where security is not a concern. + + Networked machines, and particularly those where users are granted + shell access, should say 'N' here! + Symmetric Multi-Processing support CONFIG_SMP This enables support for systems with more than one CPU. If you have diff -ruN linux-2.4.24/Documentation/kernel-parameters.txt software-suspend-linux-2.4.24-rev7/Documentation/kernel-parameters.txt --- linux-2.4.24/Documentation/kernel-parameters.txt 2004-01-22 19:47:59.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/Documentation/kernel-parameters.txt 2004-01-30 15:23:38.000000000 +1300 @@ -46,6 +46,7 @@ SERIAL Serial support is enabled. SMP The kernel is an SMP kernel. SOUND Appropriate sound system support is enabled. + SWSUSP Software suspension is enabled. V4L Video For Linux support is enabled. VGA The VGA console has been enabled. VT Virtual terminal support is enabled. @@ -424,6 +425,8 @@ initial RAM disk. nointroute [IA-64] + + noresume2 [SWSUSP] Disables resume and restore original swap space. nolapic [IA-32,APIC] Do not enable or use the local APIC. @@ -545,6 +548,8 @@ reserve= [KNL,BUGS] force the kernel to ignore some iomem area. + resume2= [SWSUSP] specifies the storage method and location for Software Suspend.. + riscom8= [HW,SERIAL] ro [KNL] Mount root device read-only on boot. diff -ruN linux-2.4.24/Documentation/swsusp.txt software-suspend-linux-2.4.24-rev7/Documentation/swsusp.txt --- linux-2.4.24/Documentation/swsusp.txt 1970-01-01 12:00:00.000000000 +1200 +++ software-suspend-linux-2.4.24-rev7/Documentation/swsusp.txt 2004-01-30 15:23:38.000000000 +1300 @@ -0,0 +1,516 @@ + --- Software Suspend ('swsusp') for Linux, version 2.0 --- + +1. What is it? +2. Why would you want it? +3. What do you need to use it? +4. How do you use it? +5. What do all those entries in /proc/swsusp do? +6. How do you get support? +7. I think I've found a bug. What should I do? +8. What about 2.6 kernels? +9. When will XXX be supported? +10. How does it work? +11. Who wrote Software Suspend? + +1. What is it? + + Imagine you're sitting at your computer, working away. For some reason, you + need to turn off your computer for a while - perhaps it's time to go home + for the day. When you come back to your computer next, you're going to want + to carry on where you left off. Now imagine that you could push a button and + have your computer store the contents of its memory to disk and power down. + Then, when you next start up your computer, it loads that image back into + memory and you can carry on from where you were, just as if you'd never + turned the computer off. Far less time to start up, no reopening + applications and finding what directory you put that file in yesterday. + That's what Software Suspend does. + +2. Why would you want it? + + Why wouldn't you want it? + + Being able to save the state of your system and quickly restore it improves + your productivity - you get a useful system in far less time than through + the normal boot process. + +3. What do you need to use it? + + a. Kernel Support. + + Software Suspend is part of the Linux Kernel. It is not part of Marcelo's + 2.4 tree at the moment, so you will need to download the kernel source and + apply the latest patches. Having done that, enable the appropriate options + in make [menu|x]config (under General Setup), compile and install your + kernel. Software Suspend is incompatible with SMP, scsi and non x86 hardware + but works with preempt support and HighMem. It requires your swap + partitions/files to be on IDE devices. + + Software Suspend patches are available from http://swsusp.sf.net. + + You may also want to apply the optional patches. At the time of writing, + option patches are available to support Bootsplash (www.bootsplash.org, for + an even nicer display during suspend), Laptop mode and Win4Lin. The laptop + mode patch is a variation on Jens Axboe's patch, which disables laptop mode + when suspending. The Win4Lin option patch provides support for Win4Lin. + + Option patches should be applied after the main patches and after Win4Lin + or Bootsplash. + + b. Swapspace. + + Software Suspend can store the suspend image in your swap partition, + a swap file or a combination thereof. Whichever combination you choose, you + will probably want to create enough swap space to store the largest image + you could have, plus the space you'd normally use for swap. A good rule of + thumb would be to calculate the amount of swap you'd want without using + Software Suspend, and then add the amount of memory you have. This swap + space can be arranged in any way you'd like. It can be in one partition or + file, or spread over a number. The only requirement is that they be active + when you start a suspend cycle. + + There is one exception to this requirement. Software Suspend has + the ability to turn on one swap file or partition at the start of + suspending and turn it back off at the end. If you want to ensure you have + enough memory to store a image when your memory is fully used, you might + want to make one swap partition/file for 'normal' use, and another for + Software Suspend to activate & deactivate automatically. (Further details + below). + + c. Bootloader configuration. + + Using Software Suspend also requires that you add an extra parameter to + your lilo.conf or equivalent. Here's an example for writing the image to a + swap partition: + + append="resume2=swap:/dev/hda1" + + This would tell Software Suspend that /dev/hda1 is a swap partition you + have. Software Suspend will use the swap signature of this partition as a + pointer to your data when you suspend. This means that (in this example) + /dev/hda1 doesn't need to be _the_ swap partition where all of your data + is actually stored. It just needs to be a pointer to a block on the disk + that has a valid swap signature. This swap partition doesn't even need to + be turned on at suspend time. + + You don't need to have a swap partition for this purpose. Software Suspend + can also use a swap file, but usage is a little more complex. Having made + your swap file, turn it on and do "cat /proc/swsusp/header_locations" + (this assumes you've already compiled your kernel with Software Suspend + support and booted it). The results of the cat command will tell you + what you need to put in lilo.conf: + + For swap partitions like /dev/hda1, simply use resume2=swap:/dev/hda1. + For swapfile `swapfile`, use resume2=swap:/dev/hda2 resume_block=0x242d. + + If the swapfile changes for any reason (it is moved to a different + location, it is deleted and recreated, or the filesystem is + defragmented) then you will have to check + /proc/swsusp/header_locations for a new resume_block value. + + Once you've compiled and installed the kernel, adjusted your lilo.conf + and rerun lilo, you should only need to reboot for the most basic part + of Software Suspend to be ready. + + As the 'swap:' portion of the "resume2=swap:/dev/hda1" applies, Suspend + supports alternative methods of storing your image. At the time of + writing, no alternatives have been implemented, but it is envisaged that + a NFS 'writer' will appear in the near future. + + d. A suspend script. + + Since 2.4 kernels don't have the driver model that's being developed for + 2.6 and 2.6 support is (currently) incomplete, you may need to do more than + just patching. Users of Software Suspend usually start the process via a + script which prepares for the suspend, tells the kernel to do its stuff and + then restore things afterwards. This script might involve: + + - Switching to a text console and back if X doesn't like the video card + status on resume. + - Running /sbin/hwclock [--directisa] to update the clock on resume + - Un/reloading PCMCIA support since it doesn't play well with swsusp. + + Note that you might not be able to unload some drivers if there are + processes using them. You might have to kill off processes that hold + devices open. Hint: if your X server accesses an USB mouse, doing a + 'chvt' to a text console releases the device and you can unload the + module. + + Check out the latest script (available on Sourceforge). + +4. How do you use it? + + Once your script is properly set up, you should just be able to start it + and everything should go like clockwork. Of course things aren't always + that easy out of the box. + + Check out (in the kernel source tree) include/linux/suspend-debug for + settings you can use to get detailed information about what swsusp is doing. + /proc/sys/kernel/swsusp and the kernel parameters swsusp_act, swsusp_dbg + and swsusp_lvl allow you to set the action and debugging parameters prior + to starting a suspend and/or at the lilo prompt before resuming. There is + also a nice little program that should be available from Sourceforge which + makes it easier to turn these debugging settings on and off. Note that to + get any debugging output, you need to enable it when compiling the kernel. + If the file /proc/swsusp/debug_sections is missing, you didn't do that. + + A neat feature of Software Suspend is possibility that you can press Escape + at any time during suspending, and the process will be aborted. Since this + will be considered a security risk in some contexts, this code is disabled + by default, but you can enable it by: + + echo 1 > /proc/swsusp/enable_escape. + + Due to the way swsusp works, this means you'll be able have your system back + and perfectly usable almost instantly. The only exception is when suspend is + at the very end of writing the image. Then it will need to reload a small + (generally less than 10% of the image size) portion first. + + If you run into problems with resuming, adding the "noresume2" option to + the kernel command line will let you skip the resume step and recover your + system. + +5. What do all those entries in /proc/swsusp do? + + /proc/swsusp is the directory which contains files you can use to tune + and configure Software Suspend to your liking. The exact contents of + the directory will depend upon the version of Software Suspend you're + running, and the options you selected at compile time. In the following + descriptions, names in brackets refer to compile time options that + control whether the file exists. (Note that they're all dependant upon + you having selected CONFIG_SOFTWARE_SUSPEND2 in the first place!) + + Since the values of these settings can open potential security risks, they + are usually accessible only to the root user. You can, however, enable a + compile time option which makes all of these files world-accessible. This + should only be done if you trust everyone with shell access to this + computer! + + - activate: + + When anything is written to this file swsusp will be activated and suspend + the system. The value is completely ignored. It is just the fact that you + write to the file that initiates the suspend. + + - async_io_limit (CONFIG_SOFTWARE_SUSPEND_SWAPWRITER): + + This value is the limit on the number of pages Software Suspend will submit + for reading or writing at once. The ideal value depends upon the speed of + your hard disks, but the default of 32 should be fine. + + - beeping: + + Set this value to 1 to hear beeps at the different stages of suspending and + resuming. + + - debug_info: + + This file returns information about your configuration that may be helpful + in diagnosing problems with suspending. + + - debug_sections (CONFIG_SOFTWARE_SUSPEND_DEBUG): + + This value, together with the console log level, controls what debugging + information is displayed. The console log level determines the level of + detail, and this value determines what detail is displayed. This value is + a bit vector, and the meaning of the bits can be found in the kernel tree + in include/linux/suspend-debug.h. It can be over-ridden using the kernel's + command line option swsusp_dbg. + + - default_console_level (CONFIG_SOFTWARE_SUSPEND_DEBUG): + + This determines the value of the console log level at the start of a + suspend cycle. If debugging is compiled in, the console log level can be + changed during a cycle by pressing the digit keys. Meanings are: + + 0: Nice display. + 1: Nice display plus numerical progress. + 2: Errors only. + 3: Low level debugging info. + 4: Medium level debugging info. + 5: High level debugging info. + 6: Verbose debugging info. + + This value can be over-ridden using the kernel command line option + swsusp_lvl. + + - disable_gzip_compression (CONFIG_SOFTWARE_SUSPEND_GZIP_COMPRESSION): + + If gzip compression support is compiled in, this option can be used to + disable this plugin. + + - disable_lzf_compression (CONFIG_SOFTWARE_SUSPEND_LZF_COMPRESSION): + + If lzf compression support is compiled in, this option can be used to + disable this plugin. + + - enable_escape: + + Setting this to "1" will enable you abort a suspend by + pressing escape, "0" (default) disables this feature. Note that enabling + this option means that you cannot initiate a suspend and then walk away + from your computer, expecting it to be secure. With feature disabled, + you can validly have this expectation once Suspend begins to write the + image to disk. (Prior to this point, it is possible that Suspend might + about because of failure to freeze all processes or because constraints + on its ability to save the image are not met). + + - expected_gzip_compression (CONFIG_SOFTWARE_SUSPEND_GZIP_COMPRESSION): + - expected_lzf_compression (CONFIG_SOFTWARE_SUSPEND_LZF_COMPRESSION): + + These values allow you to set an expected compression ratio, which Software + Suspend will use in calculating whether it meets constraints on the image + size. If this expected compression ratio is not attained, the suspend will + abort, so it is wise to allow some spare. You can see what compression + ratio is achieved in the logs after suspending. + + Note that the values are cumulative. If you compile in both gzip and lzf + compression, have both enabled, and set both expected compression ratios + to 20, Suspend will expect that the storage required will be at most + .8 * .8 = 64% of the number of pages to be written. + + - header_locations (CONFIG_SOFTWARE_SUSPEND_SWAPWRITER): + + This option tells you the resume2= options to use for swap devices you + currently have activated. It is particularly useful when you only want to + use a swap file to store your image. See above for further details. + + - image_size_limit: + + The maximum size of suspend image written to disk, measured in megabytes + (1024*1024). + + - interface_version: + + The value returned by this file can be used by scripts and configuration + tools to determine what entries should be looked for. The value is + incremented whenever an entry in /proc/swsusp is obsoleted or added. + + - last_result: + + The result of the last suspend, as defined in + include/linux/suspend-debug.h with the values SUSPEND_ABORTED to + SUSPEND_UNABLE_TO_FREE_ENOUGH_MEMORY. This is a bitmask. + + - log_everything (CONFIG_SOFTWARE_SUSPEND_DEBUG): + + Setting this option results in all messages printed being logged. Normally, + only a subset are logged, so as to not slow the process and not clutter the + logs. Useful for debugging. It can be toggled during a cycle by pressing + 'L'. + + - no_output: + + Setting this to "1" disables all output from suspend. It may be useful if a + distribution wants to implement a static display while suspending. + + - pause_between_steps (CONFIG_SOFTWARE_SUSPEND_DEBUG): + + This option is used during debugging, to make Software Suspend pause between + each step of the process. It is ignored when the nice display is on. + + - progressbar_granularity_limit (CONFIG_FBCON_SPLASHSCREEN): + + This option can be used to limit the granularity of the progress bar + displayed with a bootsplash screen. The value is the maximum number of + steps. That is, 10 will make the progress bar jump in 10% increments. + + - reboot (CONFIG_SOFTWARE_SUSPEND_DEBUG): + + This option causes Software Suspend to reboot rather than powering down + at the end of saving an image. It can be toggled during a cycle by pressing + 'R'. + + - slow: + + This option inserts a couple of one+ second delays in the code. It should + not be needed, and may disappear in a future version. + + - swapfile (CONFIG_SOFTWARE_SUSPEND_SWAPWRITER): + + This entry is used to specify the swapfile or partition that + Software Suspend will attempt to swapon/swapoff automatically. Thus, if + I normally use /dev/hda1 for swap, and want to use /dev/hda2 for specifically + for my suspend image, I would + + echo /dev/hda2 > /proc/swsusp/swapfile + + /dev/hda2 would then be automatically swapon'd and swapoff'd. Note that the + swapon and swapoff occur while other processes are frozen (including kswapd) + so this swap file will not be used up when attempting to free memory. The + parition/file is also given the highest priority, so other swapfiles/partitions + will only be used to save the image when this one is filled. + + The value of this file is used by header_locations along with any currently + activated swapfiles/partitions. + + - version: + + The version of swsusp you have compiled into the currently running kernel. + +6. How do you get support? + + Glad you asked. Software Suspend is being actively maintained and supported, + both by Nigel (the guy doing most of the coding at the moment) and its + users. You can find the mailing list via the Sourceforge project page. + +7. I think I've found a bug. What should I do? + + If you're seeing Software Suspend hang at some point, and especially if + lights are flashing on your keyboard, you should compile in debugging + support and try... + + echo 1 > /proc/swsusp/debug_sections + echo 3 > /proc/swsusp/default_console_level + echo > /proc/swsusp/activate + + You should then see low level debugging information and eventually an + oops. + + Good information on how to provide us with useful information from an + oops is found in the file REPORTING-BUGS, in the top level directory + of the kernel tree. If you get an oops, please especially note the + information about running what is printed on the screen through ksymoops. + The raw information is useless. + + You might also read the FAQ and HOWTO on the web site for known issues, + and subscribe to the mailing list. + + Beginning with 1.1rc10, you should include the contents of + /proc/swsusp/debug_info in your report. Prior to this version, similar + information is written to /var/log/messages at the end of a successful + resume and should be sent. It is also a good idea to check /var/log/messages + for relevant information as well. Information from the unloading and + reloading of drivers and modules prior to and after suspending is sometimes + helpful. + +8. What about 2.6 kernels? + + There are two versions of Software Suspend already included in the 2.6 kernel + tree. Unfortunately, they lack a large proportion of the features in 2.4. + For this reason, patches are available for the 2.6 kernel as well. You can + use the same core patches as for the 2.4 version. Work is underway to merge + this version with Patrick's code. + + Note that the existing implementations in the 2.6 kernel are the reason why + we call the parameters resume2 and noresume2. + +9. When will XXX be supported? + + Software Suspend currently lacks support for SMP, non x86 and SCSI. + + Patches for the other items (and anything that's been missed) are welcome. + Please send them to the list or directly to Nigel. + + Because Nigel's main task is definitely not Software Suspend and he doesn't + have the hardware, he will be unlikely to develop support for any of these + in the near future. His development work to date has been driven by the + desire to be a user of a more feature complete Software Suspend. + +10. How does it work? + + Software Suspend does its work in a number of steps. In the following + description, I'll talk specifically about the swapwriter, but the same + general pattern will apply to other writers that are implemented. + + a. Freezing system activity. + + The first main stage in suspending is to stop all other activity. This is + achieved in stages. First, we stop tasks from submitting new I/O using hooks + in the system calls for reading, writing and at a number of other places as + well as at the kernel threads that start I/O. If any tasks are syncing, + we wait for them to complete. We then do our own sync, just in case no + syncs were running. Next, we stop all the others tasks. Some are signalled + and put in a 'refrigerator'. Others are simply not scheduled again until we + decide to wake them up. + + b. Image preparation. + + For a successful suspend, you need to have enough disk space to store the + image and enough memory for the various limitations of Software Suspend's + algorithm. You can also specify a maximum image size. In order to attain + to those constraints, Software Suspend may 'eat' memory. If, after freezing + processes, the constraints aren't met, Software Suspend will thaw all the + other processes and begin to eat memory until its calculations indicate + the constraints are met. It will then freeze processes again and recheck + its calculations. During this cycle, it also allocates the storage needed to + save the image and prepares all the meta data that records what will be + saved and where. + + c. Storage of meta data and image. + + Software Suspend stores data in two pagesets. Pageset 2 contains pages on the + active and inactive lists; essentially the page cache. Pageset 1 contains all + other pages, including the kernel. We use two pagesets for one important + reason: We need to make an atomic copy of the kernel to ensure consistency of + the image. Without a second pagedir, we would be limited to an image that was + at most half the amount of memory available. Using two pagesets allows us to + store a full image of memory in most cases. Since pageset 2 pages won't be + needed in saving pageset 1, we first save pageset 2 pages. We can then + suspend drivers and make our atomic copy of the remaining pages using both + pageset 2 pages and any other pages that are free. While saving both + pagesets, we are careful not to corrupt the image. We immediately shoot down + pages that are added to the page cache, and we allocate a special memory pool + of extra pages that can be used by during suspending. All of the pages in + this pool are saved along with the rest of the pageset 1 pages, even if + they're not used. This saves us having to worry about the image becoming + inconsistent while we're saving pageset 2. + + d. Save a second copy of the pagedirs. + + To reload pagedir 1 at resume time, we need to know where the data is + stored. This requires the saving of a second copy of the pagedirs. + + e. Save the suspend header. + + Nearly there! We save our settings and other parameters needed for + reloading pagedir 1 in a 'suspend header' this is a single swap page. + + f. Set the swap header. + + Finally, we edit the swap header for our resume2= swap file/partition. The + swap signature is changed to record what kind of header it originally was + (swapspace 1 or 2) and the bdev and first block and block size details of + the suspend header. + + g. Power down. + + Or reboot if we're debugging and the appropriate option is selected. + + Whew! + + Reloading the image. + -------------------- + + Reloading the image is essentially the reverse of all the above. We load + our copy of pagedir 1, being careful to choose locations that aren't going + to be overwritten as we copy it back (We start very early in the boot + process, so there are no other processes to quiesce here). We then copy + pagedir 1 back to its original location in memory and restore the process + context. We are now running with the original kernel. Next, we reload the + pageset 2 pages, free the memory and swap used by Software Suspend, restore + the pagedir header and restart processes. Sounds easy in comparison to + suspending, doesn't it! + + There is of course more to Software Suspend than this, but this explanation + should be a good start. If there's interest, I'll write further + documentation on range pages and the low level I/O. + +11. Who wrote Software Suspend? + + (Answer based on the writings of Florent Chabaud, credits in files and + Nigel's limited knowledge; apologies to anyone missed out!) + + The main developers of Software Suspend have been... + + Gabor Kuti + Pavel Machek + Florent Chabaud + Nigel Cunningham + + They have been aided in their efforts by a host of hundreds, if not thousands + of testers and people who have submitted bug fixes & suggestions. Of special + note are the efforts of Michael Frank, who had his computers repetitively + suspend and resume for literally tens of thousands of cycles and developed + scripts to stress the system and test Software Suspend far beyond the point + most of us (Nigel included!) would consider testing. His efforts have + contributed as much to Software Suspend as any of the names above. diff -ruN linux-2.4.24/Documentation/sysctl/kernel.txt software-suspend-linux-2.4.24-rev7/Documentation/sysctl/kernel.txt --- linux-2.4.24/Documentation/sysctl/kernel.txt 2004-01-22 19:48:07.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/Documentation/sysctl/kernel.txt 2004-01-30 15:23:38.000000000 +1300 @@ -39,6 +39,7 @@ - rtsig-max - sg-big-buff [ generic SCSI device (sg) ] - shmmax [ sysv ipc ] +- swsusp ==> Documentation/swsusp.txt - tainted - version - zero-paged [ PPC only ] @@ -221,6 +222,12 @@ ============================================================== +swsusp: + +Please see Documentation/swsusp.txt for up-to-date documentation. + +============================================================== + tainted: Non-zero if the kernel has been tainted. Numeric values, which diff -ruN linux-2.4.24/drivers/acpi/system.c software-suspend-linux-2.4.24-rev7/drivers/acpi/system.c --- linux-2.4.24/drivers/acpi/system.c 2004-01-22 19:46:59.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/drivers/acpi/system.c 2004-01-30 15:23:38.000000000 +1300 @@ -48,6 +48,7 @@ #include #endif #endif +#include #define _COMPONENT ACPI_SYSTEM_COMPONENT @@ -134,7 +135,10 @@ * interrupts. */ #ifdef CONFIG_X86 - init_8259A(0); +#ifdef CONFIG_SOFTWARE_SUSPEND2 + if (state != ACPI_STATE_S4) +#endif + init_8259A(0); #endif /* wait for power to come back */ mdelay(1000); @@ -311,7 +315,7 @@ u32 state) { acpi_status status; - + int swsusp=0; /* only support S1 and S5 on kernel 2.4 */ if (state != ACPI_STATE_S1 && state != ACPI_STATE_S4 && state != ACPI_STATE_S5) @@ -319,6 +323,9 @@ if (ACPI_STATE_S4 == state) { +#ifdef CONFIG_SOFTWARE_SUSPEND2 + swsusp=1; +#else /* For s4bios, we need a wakeup address. */ if (1 == acpi_gbl_FACS->S4bios_f && 0 != acpi_gbl_FADT->smi_cmd) { @@ -328,6 +335,7 @@ } else /* We don't support S4 under 2.4. Give up */ return AE_ERROR; +#endif } status = acpi_system_save_state(state); @@ -341,12 +349,17 @@ ACPI_FLUSH_CPU_CACHE(); /* perform OS-specific sleep actions */ - status = acpi_system_suspend(state); - - /* Even if we failed to go to sleep, all of the devices are in an suspended - * mode. So, we run these unconditionaly to make sure we have a usable system - * no matter what. - */ + if(swsusp) { /* we just ignore acpi architecture for the moment */ + software_suspend_pending(); /* when we return, this is resume */ + status = AE_OK; + } else { + status = acpi_system_suspend(state); + + /* Even if we failed to go to sleep, all of the devices are in an suspended + * mode. So, we run these unconditionaly to make sure we have a usable system + * no matter what. + */ + } acpi_leave_sleep_state(state); acpi_system_restore_state(state); @@ -391,10 +404,15 @@ for (i=0; istates[i]) { p += sprintf(p, "S%d ", i); - if (i == ACPI_STATE_S4 && - acpi_gbl_FACS->S4bios_f && - 0 != acpi_gbl_FADT->smi_cmd) - p += sprintf(p, "S4Bios "); + if (i == ACPI_STATE_S4) { +#ifdef CONFIG_SOFTWARE_SUSPEND2 + p += sprintf(p, "(swsusp) "); +#else + if(acpi_gbl_FACS->S4bios_f && + 0 != acpi_gbl_FADT->smi_cmd) + p += sprintf(p, "(Bios) "); +#endif + } } } p += sprintf(p, "\n"); @@ -697,9 +715,15 @@ for (i = 0; i <= ACPI_STATE_S5; i++) { if (system->states[i]) { p += sprintf(p,"S%d ", i); - if (i == ACPI_STATE_S4 && acpi_gbl_FACS->S4bios_f && - acpi_gbl_FADT->smi_cmd != 0) - p += sprintf(p, "S4Bios "); + if (i == ACPI_STATE_S4) { +#ifdef CONFIG_SOFTWARE_SUSPEND2 + p += sprintf(p, "(swsusp) "); +#else + if(acpi_gbl_FACS->S4bios_f && + acpi_gbl_FADT->smi_cmd != 0) + p += sprintf(p, "(Bios) "); +#endif + } } } @@ -1261,10 +1285,16 @@ case ACPI_STATE_S4: if (acpi_gbl_FACS->S4bios_f && 0 != acpi_gbl_FADT->smi_cmd) { - printk(" S4bios"); + printk(" S4 (bios)"); + system->states[i] = 1; + } +#ifdef CONFIG_SOFTWARE_SUSPEND2 + else { + printk(" S4 (swsusp)"); system->states[i] = 1; } - /* no break */ +#endif + break; default: if (ACPI_SUCCESS(status)) { system->states[i] = 1; diff -ruN linux-2.4.24/drivers/block/loop.c software-suspend-linux-2.4.24-rev7/drivers/block/loop.c --- linux-2.4.24/drivers/block/loop.c 2004-01-22 19:48:35.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/drivers/block/loop.c 2004-01-30 15:23:38.000000000 +1300 @@ -581,7 +581,9 @@ atomic_inc(&lo->lo_pending); spin_unlock_irq(&lo->lo_lock); - current->flags |= PF_NOIO; + current->flags |= PF_NOIO | PF_NOFREEZE; /* loop can be used in an encrypted device + hence, it mustn't be stopped at all because it could + be indirectly used during suspension */ /* * up sem, we are running diff -ruN linux-2.4.24/drivers/char/agp/agpgart_be.c software-suspend-linux-2.4.24-rev7/drivers/char/agp/agpgart_be.c --- linux-2.4.24/drivers/char/agp/agpgart_be.c 2004-01-22 19:45:55.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/drivers/char/agp/agpgart_be.c 2004-01-30 15:23:38.000000000 +1300 @@ -589,7 +589,7 @@ agp_bridge.gatt_table_real = (u32 *) table; agp_gatt_table = (void *)table; -#ifdef CONFIG_X86 +#if defined(CONFIG_X86) && !defined(CONFIG_SOFTWARE_SUSPEND2) err = change_page_attr(virt_to_page(table), 1<real)); CACHE_FLUSH(); -#ifdef CONFIG_X86 +#if defined(CONFIG_X86) && !defined(CONFIG_SOFTWARE_SUSPEND2) err = change_page_attr(virt_to_page(page_map->real), 1, PAGE_KERNEL_NOCACHE); #endif if (!err) @@ -3066,7 +3062,7 @@ static void amd_free_page_map(amd_page_map *page_map) { iounmap(page_map->remapped); -#ifdef CONFIG_X86 +#if defined(CONFIG_X86) && !defined(CONFIG_SOFTWARE_SUSPEND2) change_page_attr(virt_to_page(page_map->real), 1, PAGE_KERNEL); #endif ClearPageReserved(virt_to_page(page_map->real)); @@ -4202,7 +4198,7 @@ return -ENOMEM; } SetPageReserved(virt_to_page(page_map->real)); -#ifdef CONFIG_X86 +#if defined(CONFIG_X86) && !defined(CONFIG_SOFTWARE_SUSPEND2) err = change_page_attr(virt_to_page(page_map->real), 1, PAGE_KERNEL_NOCACHE); #endif CACHE_FLUSH(); @@ -4226,7 +4222,7 @@ static void serverworks_free_page_map(serverworks_page_map *page_map) { -#ifdef CONFIG_X86 +#if defined(CONFIG_X86) && !defined(CONFIG_SOFTWARE_SUSPEND2) change_page_attr(virt_to_page(page_map->real),1,PAGE_KERNEL); #endif iounmap(page_map->remapped); diff -ruN linux-2.4.24/drivers/char/console.c software-suspend-linux-2.4.24-rev7/drivers/char/console.c --- linux-2.4.24/drivers/char/console.c 2004-01-22 19:50:04.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/drivers/char/console.c 2004-01-30 15:23:38.000000000 +1300 @@ -149,11 +149,11 @@ static void blank_screen(unsigned long dummy); static void gotoxy(int currcons, int new_x, int new_y); static void save_cur(int currcons); -static void reset_terminal(int currcons, int do_clear); +void reset_terminal(int currcons, int do_clear); static void con_flush_chars(struct tty_struct *tty); static void set_vesa_blanking(unsigned long arg); static void set_cursor(int currcons); -static void hide_cursor(int currcons); +void hide_cursor(int currcons); static void unblank_screen_t(unsigned long dummy); static void console_callback(void *ignored); @@ -531,7 +531,7 @@ sw->con_putc(vc_cons[currcons].d, i, y, x); } -static void hide_cursor(int currcons) +void hide_cursor(int currcons) { if (currcons == sel_cons) clear_selection(); @@ -1398,7 +1398,7 @@ ESpalette }; /* console_sem is held (except via vc_init()) */ -static void reset_terminal(int currcons, int do_clear) +void reset_terminal(int currcons, int do_clear) { top = 0; bottom = video_num_lines; @@ -2991,7 +2991,7 @@ void putconsxy(int currcons, char *p) { - gotoxy(currcons, p[0], p[1]); + gotoxy(currcons, (unsigned char) p[0], (unsigned char) p[1]); set_cursor(currcons); } @@ -3038,6 +3038,8 @@ EXPORT_SYMBOL(vc_resize); EXPORT_SYMBOL(fg_console); EXPORT_SYMBOL(console_blank_hook); +EXPORT_SYMBOL(hide_cursor); +EXPORT_SYMBOL(reset_terminal); #ifdef CONFIG_VT EXPORT_SYMBOL(vt_cons); #endif diff -ruN linux-2.4.24/drivers/char/hvc_console.c software-suspend-linux-2.4.24-rev7/drivers/char/hvc_console.c --- linux-2.4.24/drivers/char/hvc_console.c 2004-01-22 19:46:19.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/drivers/char/hvc_console.c 2004-01-30 15:23:38.000000000 +1300 @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -230,17 +231,23 @@ int khvcd(void *unused) { int i; + DECLARE_SWSUSP_LOCAL_VAR; daemonize(); reparent_to_init(); strcpy(current->comm, "khvcd"); sigfillset(¤t->blocked); + SWSUSP_THREAD_FLAGS_RESET; + SWSUSP_ACTIVITY_START(PF_SYNCTHREAD); + for (;;) { for (i = 0; i < MAX_NR_HVC_CONSOLES; ++i) hvc_poll(i); + SWSUSP_ACTIVITY_PAUSING; set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(TIMEOUT); + SWSUSP_ACTIVITY_RESTARTING(PF_SYNCTHREAD); } } diff -ruN linux-2.4.24/drivers/char/keyboard.c software-suspend-linux-2.4.24-rev7/drivers/char/keyboard.c --- linux-2.4.24/drivers/char/keyboard.c 2004-01-22 19:46:59.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/drivers/char/keyboard.c 2004-01-30 15:23:38.000000000 +1300 @@ -32,6 +32,9 @@ #include #include #include +#include +#include +#include #include #include @@ -152,7 +155,11 @@ struct pt_regs * kbd_pt_regs; #ifdef CONFIG_MAGIC_SYSRQ -static int sysrq_pressed; +int sysrq_pressed; /* Made non static so swsusp can reset on resume */ +#endif + +#ifdef CONFIG_PM +extern void wakeup_suspend(void); #endif static struct pm_dev *pm_kbd; @@ -275,6 +282,85 @@ } #endif +#ifdef CONFIG_PM + if (down && (software_suspend_state & SOFTWARE_SUSPEND_RUNNING)) { +#ifdef CONFIG_SOFTWARE_SUSPEND2 + extern void prepare_status(int printalways, int clearbar, const char *fmt, ...); + extern unsigned long swsusp_action; + extern void request_abort_suspend(void); + extern void machine_restart(char * __unused); + if (software_suspend_state & SOFTWARE_SUSPEND_SANITY_CHECK_PROMPT) { + if (keycode == 57) + machine_restart(NULL); + else if (keycode == 46) + wakeup_suspend(); + else + goto out; + } + switch (keycode) { + case 60: + case 1: + /* Abort suspend */ + if (TEST_ACTION_STATE(SUSPEND_CAN_CANCEL)) + request_abort_suspend(); + break; + case 2: + console_loglevel = 1; + break; + case 11: + console_loglevel = 0; + break; +#ifdef CONFIG_SOFTWARE_SUSPEND_DEBUG + case 25: + /* During suspend, toggle pausing with P */ + swsusp_action ^= (1 << SUSPEND_PAUSE); + prepare_status(1, 0, "Pausing %s.\n", + TEST_ACTION_STATE(SUSPEND_PAUSE) ? "enabled" : "disabled"); + if (!TEST_ACTION_STATE(SUSPEND_PAUSE)) + wakeup_suspend(); + break; + case 19: + /* Otherwise, if R pressed, toggle rebooting */ + swsusp_action ^= (1 << SUSPEND_REBOOT); + prepare_status(1, 0, "Rebooting %s.\n", + TEST_ACTION_STATE(SUSPEND_REBOOT) ? "enabled" : "disabled"); + break; + case 31: + /* Otherwise, if S pressed, toggle single step */ + swsusp_action ^= (1 << SUSPEND_SINGLESTEP); + prepare_status(1, 0, "Single step %s.\n", + TEST_ACTION_STATE(SUSPEND_SINGLESTEP) ? "enabled" : "disabled"); + if (!TEST_ACTION_STATE(SUSPEND_SINGLESTEP)) + wakeup_suspend(); + break; + case 38: + /* Otherwise, if L pressed, toggle logging everything */ + swsusp_action ^= (1 << SUSPEND_LOGALL); + prepare_status(1, 0, "Logging all output %s.\n", + TEST_ACTION_STATE(SUSPEND_LOGALL) ? "enabled" : "disabled"); + break; + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + case 10: + console_loglevel = ((keycode - 1)); + break; + default: + prepare_status(1, 0, "Key %d doesn't do anything.\n", keycode); +#endif + } +#endif + if (keycode == 57) + wakeup_suspend(); + + goto out; + } +#endif + if (kbd->kbdmode == VC_MEDIUMRAW) { /* soon keycodes will require more than one byte */ put_queue(keycode + up_flag); diff -ruN linux-2.4.24/drivers/char/n_tty.c software-suspend-linux-2.4.24-rev7/drivers/char/n_tty.c --- linux-2.4.24/drivers/char/n_tty.c 2004-01-22 19:45:39.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/drivers/char/n_tty.c 2004-01-30 15:23:38.000000000 +1300 @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -1153,6 +1154,7 @@ DECLARE_WAITQUEUE(wait, current); int c; ssize_t retval = 0; + DECLARE_SWSUSP_LOCAL_VAR; /* Job control check -- must be done at start (POSIX.1 7.1.1.4). */ if (L_TOSTOP(tty) && @@ -1209,7 +1211,9 @@ retval = -EAGAIN; break; } + SWSUSP_ACTIVITY_PAUSING; schedule(); + SWSUSP_ACTIVITY_RESTARTING(PF_SYNCTHREAD); } break_out: current->state = TASK_RUNNING; diff -ruN linux-2.4.24/drivers/char/pc_keyb.c software-suspend-linux-2.4.24-rev7/drivers/char/pc_keyb.c --- linux-2.4.24/drivers/char/pc_keyb.c 2004-01-22 19:50:40.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/drivers/char/pc_keyb.c 2004-01-30 15:23:38.000000000 +1300 @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -1105,18 +1106,22 @@ DECLARE_WAITQUEUE(wait, current); ssize_t i = count; unsigned char c; + DECLARE_SWSUSP_LOCAL_VAR; if (queue_empty()) { if (file->f_flags & O_NONBLOCK) return -EAGAIN; add_wait_queue(&queue->proc_list, &wait); repeat: + SWSUSP_ACTIVITY_PAUSING; set_current_state(TASK_INTERRUPTIBLE); if (queue_empty() && !signal_pending(current)) { schedule(); + SWSUSP_ACTIVITY_RESTARTING(0); goto repeat; } current->state = TASK_RUNNING; + SWSUSP_ACTIVITY_RESTARTING(0); remove_wait_queue(&queue->proc_list, &wait); } while (i > 0 && !queue_empty()) { diff -ruN linux-2.4.24/drivers/char/serial.c software-suspend-linux-2.4.24-rev7/drivers/char/serial.c --- linux-2.4.24/drivers/char/serial.c 2004-01-22 19:46:19.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/drivers/char/serial.c 2004-01-30 15:23:38.000000000 +1300 @@ -62,10 +62,14 @@ * Robert Schwebel , * Juergen Beisert , * Theodore Ts'o + * + * 01/04: Verion: 5.1 + * Add PM suspend/resume support + * Michael Frank */ -static char *serial_version = "5.05c"; -static char *serial_revdate = "2001-07-08"; +static char *serial_version = "5.1"; +static char *serial_revdate = "2004-01-29"; /* * Serial driver configuration section. Here are the various options: @@ -203,6 +207,9 @@ #include #include #include +#ifdef CONFIG_PM +#include +#endif #if (LINUX_VERSION_CODE >= 131343) #include #endif @@ -248,6 +255,16 @@ #define _INLINE_ #endif +#if defined(CONFIG_SERIAL_CONSOLE) +#include +#include +#include + +#ifdef CONFIG_PM +extern void wakeup_suspend(void); +#endif +#endif + static char *serial_name = "Serial driver"; static DECLARE_TASK_QUEUE(tq_serial); @@ -638,7 +655,8 @@ else if (*status & UART_LSR_FE) *tty->flip.flag_buf_ptr = TTY_FRAME; } -#if defined(CONFIG_SERIAL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#if defined(CONFIG_SERIAL_CONSOLE) +#if defined(CONFIG_MAGIC_SYSRQ) if (break_pressed && info->line == sercons.index) { if (ch != 0 && time_before(jiffies, break_pressed + HZ*5)) { @@ -648,7 +666,88 @@ } break_pressed = 0; } +#endif /* CONFIG_MAGIC_SYSRQ */ + +#ifdef CONFIG_PM + if ((software_suspend_state & (SOFTWARE_SUSPEND_SANITY_CHECK_PROMPT | SOFTWARE_SUSPEND_RUNNING))) { +#ifdef CONFIG_SOFTWARE_SUSPEND2 + extern void prepare_status(int printalways, int clearbar, const char *fmt, ...); + extern unsigned long swsusp_action; + extern void request_abort_suspend(void); + extern void machine_restart(char * __unused); + + /* + * I would like to be able to do the same as + * drivers/char/keyboard.c here, and allow responding to the + * sanity check prompt over a serial console. Unfortunately, + * it's one way communication at this point. You can't even + * get into kdb because the serial port interrupts aren't + * running yet. + */ + switch (ch) { + case 27: + /* Abort suspend */ + if (TEST_ACTION_STATE(SUSPEND_CAN_CANCEL)) + request_abort_suspend(); + break; + case 49: + console_loglevel = 1; + break; + case 48: + console_loglevel = 0; + break; +#ifdef CONFIG_SOFTWARE_SUSPEND_DEBUG + case 80: + case 112: + /* During suspend, toggle pausing with Pause or Break if kdb active */ + swsusp_action ^= (1 << SUSPEND_PAUSE); + prepare_status(1, 0, "Pausing %s.\n", + TEST_ACTION_STATE(SUSPEND_PAUSE) ? "enabled" : "disabled"); + if (!TEST_ACTION_STATE(SUSPEND_PAUSE)) + wakeup_suspend(); + break; + case 82: + case 114: + /* Otherwise, if R pressed, toggle rebooting */ + swsusp_action ^= (1 << SUSPEND_REBOOT); + prepare_status(1, 0, "Rebooting %s.\n", + TEST_ACTION_STATE(SUSPEND_REBOOT) ? "enabled" : "disabled"); + break; + case 83: + case 115: + /* Otherwise, if S pressed, toggle single-stepping */ + swsusp_action ^= (1 << SUSPEND_SINGLESTEP); + prepare_status(1, 0, "Single step %s.\n", + TEST_ACTION_STATE(SUSPEND_SINGLESTEP) ? "enabled" : "disabled"); + if (!TEST_ACTION_STATE(SUSPEND_SINGLESTEP)) + wakeup_suspend(); + break; + case 76: + case 108: + /* Otherwise, if L pressed, toggle logging everything */ + swsusp_action ^= (1 << SUSPEND_LOGALL); + prepare_status(1, 0, "Logging all output %s.\n", + TEST_ACTION_STATE(SUSPEND_LOGALL) ? "enabled" : "disabled"); + break; + case 50: + case 51: + case 52: + case 53: + case 54: + case 55: + console_loglevel = ((ch - 48)); + break; +#endif + } #endif + if (ch == 32) + wakeup_suspend(); + + goto ignore_char; + } +#endif /* CONFIG_PM */ +#endif /* CONFIG_SERIAL_CONSOLE */ + if ((*status & info->ignore_status_mask) == 0) { tty->flip.flag_buf_ptr++; tty->flip.char_buf_ptr++; @@ -666,7 +765,7 @@ tty->flip.flag_buf_ptr++; tty->flip.char_buf_ptr++; } -#if defined(CONFIG_SERIAL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#if defined(CONFIG_SERIAL_CONSOLE) && ( defined(CONFIG_MAGIC_SYSRQ) || defined(CONFIG_PM)) ignore_char: #endif *status = serial_inp(info, UART_LSR); @@ -1208,44 +1307,16 @@ } #endif /* CONFIG_SERIAL_RSA */ -static int startup(struct async_struct * info) +/* + * Starts the uart + */ + +int start_uart(struct async_struct * info) { - unsigned long flags; int retval=0; - void (*handler)(int, void *, struct pt_regs *); struct serial_state *state= info->state; - unsigned long page; -#ifdef CONFIG_SERIAL_MANY_PORTS - unsigned short ICP; -#endif - - page = get_zeroed_page(GFP_KERNEL); - if (!page) - return -ENOMEM; - - save_flags(flags); cli(); - if (info->flags & ASYNC_INITIALIZED) { - free_page(page); - goto errout; - } - - if (!CONFIGURED_SERIAL_PORT(state) || !state->type) { - if (info->tty) - set_bit(TTY_IO_ERROR, &info->tty->flags); - free_page(page); - goto errout; - } - if (info->xmit.buf) - free_page(page); - else - info->xmit.buf = (unsigned char *) page; - -#ifdef SERIAL_DEBUG_OPEN - printk("starting up ttys%d (irq %d)...", info->line, state->irq); -#endif - - if (uart_config[state->type].flags & UART_STARTECH) { + if (uart_config[state->type].flags & UART_STARTECH) { /* Wake up UART */ serial_outp(info, UART_LCR, 0xBF); serial_outp(info, UART_EFR, UART_EFR_ECB); @@ -1341,69 +1412,19 @@ retval = -ENODEV; goto errout; } - - /* - * Allocate the IRQ if necessary - */ - if (state->irq && (!IRQ_ports[state->irq] || - !IRQ_ports[state->irq]->next_port)) { - if (IRQ_ports[state->irq]) { -#ifdef CONFIG_SERIAL_SHARE_IRQ - free_irq(state->irq, &IRQ_ports[state->irq]); -#ifdef CONFIG_SERIAL_MULTIPORT - if (rs_multiport[state->irq].port1) - handler = rs_interrupt_multi; - else -#endif - handler = rs_interrupt; -#else - retval = -EBUSY; - goto errout; -#endif /* CONFIG_SERIAL_SHARE_IRQ */ - } else - handler = rs_interrupt_single; - - retval = request_irq(state->irq, handler, SA_SHIRQ, - "serial", &IRQ_ports[state->irq]); - if (retval) { - if (capable(CAP_SYS_ADMIN)) { - if (info->tty) - set_bit(TTY_IO_ERROR, - &info->tty->flags); - retval = 0; - } - goto errout; - } - } +errout: + return retval; +} - /* - * Insert serial port into IRQ chain. - */ - info->prev_port = 0; - info->next_port = IRQ_ports[state->irq]; - if (info->next_port) - info->next_port->prev_port = info; - IRQ_ports[state->irq] = info; - figure_IRQ_timeout(state->irq); +int init_uart(struct async_struct * info) +{ + int retval=0; +#ifdef CONFIG_SERIAL_MANY_PORTS + unsigned short ICP; +#endif - /* - * Now, initialize the UART - */ serial_outp(info, UART_LCR, UART_LCR_WLEN8); /* reset DLAB */ - info->MCR = 0; - if (info->tty->termios->c_cflag & CBAUD) - info->MCR = UART_MCR_DTR | UART_MCR_RTS; -#ifdef CONFIG_SERIAL_MANY_PORTS - if (info->flags & ASYNC_FOURPORT) { - if (state->irq == 0) - info->MCR |= UART_MCR_OUT1; - } else -#endif - { - if (state->irq != 0) - info->MCR |= UART_MCR_OUT2; - } info->MCR |= ALPHA_KLUDGE_MCR; /* Don't ask */ serial_outp(info, UART_MCR, info->MCR); @@ -1459,6 +1480,108 @@ * and set the speed of the serial port */ change_speed(info, 0); + return retval; +} + +static int startup(struct async_struct * info) +{ + unsigned long flags; + int retval=0; + void (*handler)(int, void *, struct pt_regs *); + struct serial_state *state=info->state; + unsigned long page; + + page = get_zeroed_page(GFP_KERNEL); + if (!page) + return -ENOMEM; + + save_flags(flags); cli(); + + if (info->flags & ASYNC_INITIALIZED) { + free_page(page); + goto errout; + } + + if (!CONFIGURED_SERIAL_PORT(state) || !state->type) { + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + free_page(page); + goto errout; + } + if (info->xmit.buf) + free_page(page); + else + info->xmit.buf = (unsigned char *) page; + +#ifdef SERIAL_DEBUG_OPEN + printk("starting up ttys%d (irq %d)...", info->line, state->irq); +#endif + + info->MCR = 0; + if (info->tty->termios->c_cflag & CBAUD) + info->MCR = UART_MCR_DTR | UART_MCR_RTS; +#ifdef CONFIG_SERIAL_MANY_PORTS + if (info->flags & ASYNC_FOURPORT) { + if (state->irq == 0) + info->MCR |= UART_MCR_OUT1; + } else +#endif + { + if (state->irq != 0) + info->MCR |= UART_MCR_OUT2; + } + + start_uart(info); + + /* + * Allocate the IRQ if necessary + */ + if (state->irq && (!IRQ_ports[state->irq] || + !IRQ_ports[state->irq]->next_port)) { + if (IRQ_ports[state->irq]) { +#ifdef CONFIG_SERIAL_SHARE_IRQ + free_irq(state->irq, &IRQ_ports[state->irq]); +#ifdef CONFIG_SERIAL_MULTIPORT + if (rs_multiport[state->irq].port1) + handler = rs_interrupt_multi; + else +#endif + handler = rs_interrupt; +#else + retval = -EBUSY; + goto errout; +#endif /* CONFIG_SERIAL_SHARE_IRQ */ + } else + handler = rs_interrupt_single; + + retval = request_irq(state->irq, handler, SA_SHIRQ, + "serial", &IRQ_ports[state->irq]); + if (retval) { + if (capable(CAP_SYS_ADMIN)) { + if (info->tty) + set_bit(TTY_IO_ERROR, + &info->tty->flags); + retval = 0; + } + goto errout; + } + } + + /* + * Insert serial port into IRQ chain. + */ + info->prev_port = 0; + info->next_port = IRQ_ports[state->irq]; + if (info->next_port) + info->next_port->prev_port = info; + IRQ_ports[state->irq] = info; + figure_IRQ_timeout(state->irq); + + /* + * Now, initialize the UART + */ + + init_uart(info); info->flags |= ASYNC_INITIALIZED; restore_flags(flags); @@ -1470,6 +1593,33 @@ } /* + * Sleep capable UART + */ + +static void sleep_uart(struct async_struct * info) +{ + (void)serial_in(info, UART_RX); /* read data port to reset things */ + + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + + if (uart_config[info->state->type].flags & UART_STARTECH) { + /* Arrange to enter sleep mode */ + serial_outp(info, UART_LCR, 0xBF); + serial_outp(info, UART_EFR, UART_EFR_ECB); + serial_outp(info, UART_LCR, 0); + serial_outp(info, UART_IER, UART_IERX_SLEEP); + serial_outp(info, UART_LCR, 0xBF); + serial_outp(info, UART_EFR, 0); + serial_outp(info, UART_LCR, 0); + } + if (info->state->type == PORT_16750) { + /* Arrange to enter sleep mode */ + serial_outp(info, UART_IER, UART_IERX_SLEEP); + } +} + +/* * This routine will shutdown a serial port; interrupts are disabled, and * DTR is dropped if the hangup on close termio flag is on. */ @@ -1566,27 +1716,9 @@ disable_rsa(info))) state->baud_base = SERIAL_RSA_BAUD_BASE_LO; #endif - - (void)serial_in(info, UART_RX); /* read data port to reset things */ - - if (info->tty) - set_bit(TTY_IO_ERROR, &info->tty->flags); + sleep_uart(info); - if (uart_config[info->state->type].flags & UART_STARTECH) { - /* Arrange to enter sleep mode */ - serial_outp(info, UART_LCR, 0xBF); - serial_outp(info, UART_EFR, UART_EFR_ECB); - serial_outp(info, UART_LCR, 0); - serial_outp(info, UART_IER, UART_IERX_SLEEP); - serial_outp(info, UART_LCR, 0xBF); - serial_outp(info, UART_EFR, 0); - serial_outp(info, UART_LCR, 0); - } - if (info->state->type == PORT_16750) { - /* Arrange to enter sleep mode */ - serial_outp(info, UART_IER, UART_IERX_SLEEP); - } info->flags &= ~ASYNC_INITIALIZED; restore_flags(flags); } @@ -3250,6 +3382,110 @@ return 0; } +#ifdef CONFIG_PM + +/* + * Suspend an UART + */ + +static int rs_suspend_uart(struct async_struct *info) +{ + int retval = 0; + printk("serial.c: Suspending %lx\n", info->port); + + /* disable all intrs */ + serial_outp(info, UART_IER, 0x00); + + /* disable break condition */ + serial_out(info, UART_LCR, serial_inp(info, UART_LCR) & ~UART_LCR_SBC); + + /* Disable line */ + if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) + serial_outp(info, UART_MCR,(info->MCR & ~(UART_MCR_DTR|UART_MCR_RTS))); + + /* put UART to sleep */ + sleep_uart(info); + + if (retval) + info->flags &= ~ASYNC_INITIALIZED; + return retval; +} +static int rs_suspend(struct tty_struct *tty) +{ + int retval = 0; + unsigned long flags; + save_flags(flags); cli(); + + retval = rs_suspend_uart((struct async_struct *)tty->driver_data); + + restore_flags(flags); + return retval; +} + +/* + * Resume an UART + */ + +/* Todo: eval multiport interrupt resume */ + +static int rs_resume_uart(struct async_struct *info) +{ + int retval = 0; + + printk("serial.c: Resuming %lx\n", info->port); + + if ((retval = start_uart(info))) + goto out; + retval = init_uart(info); +out: + if (retval) + info->flags &= ~ASYNC_INITIALIZED; + return retval; +} + +static int rs_resume(struct tty_struct *tty) +{ + int retval = 0; + unsigned long flags; + save_flags(flags); cli(); + + retval = rs_resume_uart((struct async_struct *)tty->driver_data); + + restore_flags(flags); + return retval; +} + +/* + * Suspend or Resume all UARTs + */ + +static int rs_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data) +{ + struct serial_state *state = rs_table; + int i; + unsigned long flags; /* Interrupts will be reenabled once per iteration */ + + for (i =0; i < NR_PORTS; i++, state++) { + if (!state->info) /* info is NULL unless port opened */ + continue; + if (state->type == PORT_UNKNOWN) /* Skip unknown/unregistered ports */ + continue; + + save_flags(flags); cli(); + + switch (rqst) { + case PM_SUSPEND: + rs_suspend_uart(state->info); + break; + case PM_RESUME: + rs_resume_uart(state->info); + } + restore_flags(flags); + } + return 0; +} +#endif + /* * /proc fs routines.... */ @@ -5475,6 +5711,10 @@ serial_driver.wait_until_sent = rs_wait_until_sent; serial_driver.read_proc = rs_read_proc; #endif +#ifdef CONFIG_PM + serial_driver.suspend = rs_suspend; + serial_driver.resume = rs_resume; +#endif /* * The callout device is just like normal device except for @@ -5554,8 +5794,13 @@ probe_serial_pci(); #endif #ifdef ENABLE_SERIAL_PNP - probe_serial_pnp(); + probe_serial_pnp(); +#endif +#ifdef CONFIG_PM + if (!pm_register(PM_UNKNOWN_DEV, 0, rs_pm_callback)) + panic("Couldn't register with PM subsystem\n"); #endif + return 0; } @@ -5726,6 +5971,9 @@ struct async_struct *info; /* printk("Unloading %s: version %s\n", serial_name, serial_version); */ +#ifdef CONFIG_PM + pm_unregister_all(rs_pm_callback); +#endif del_timer_sync(&serial_timer); save_flags(flags); cli(); remove_bh(SERIAL_BH); diff -ruN linux-2.4.24/drivers/char/sysrq.c software-suspend-linux-2.4.24-rev7/drivers/char/sysrq.c --- linux-2.4.24/drivers/char/sysrq.c 2004-01-22 19:50:07.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/drivers/char/sysrq.c 2004-01-30 15:23:38.000000000 +1300 @@ -27,6 +27,7 @@ #include #include #include +#include #include @@ -47,7 +48,8 @@ int i; i = key - '0'; console_loglevel = 7; - printk("Loglevel set to %d\n", i); + if (!suspend_task) + printk("Loglevel set to %d\n", i); console_loglevel = i; } static struct sysrq_key_op sysrq_loglevel_op = { @@ -137,8 +139,19 @@ static void go_sync(struct super_block *sb, int remount_flag) { int orig_loglevel; + +#ifdef CONFIG_SOFTWARE_SUSPEND2 + if (suspend_task) { + printk(KERN_INFO "Not %sing device %s. Suspend may have used memory with dirty data!", + remount_flag ? "remount" : "sync", + kdevname(sb->s_dev)); + return; + } +#endif + orig_loglevel = console_loglevel; console_loglevel = 7; + printk(KERN_INFO "%sing device %s ... ", remount_flag ? "Remount" : "Sync", kdevname(sb->s_dev)); @@ -442,7 +455,8 @@ return; orig_log_level = console_loglevel; - console_loglevel = 7; + if (!suspend_task) /* Not if nice display on */ + console_loglevel = 7; printk(KERN_INFO "SysRq : "); op_p = __sysrq_get_key_op(key); diff -ruN linux-2.4.24/drivers/char/tty_io.c software-suspend-linux-2.4.24-rev7/drivers/char/tty_io.c --- linux-2.4.24/drivers/char/tty_io.c 2004-01-22 19:48:42.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/drivers/char/tty_io.c 2004-01-30 15:23:38.000000000 +1300 @@ -90,6 +90,7 @@ #include #include #include +#include #include #include @@ -655,6 +656,7 @@ int i; struct tty_struct * tty; struct inode *inode; + DECLARE_SWSUSP_LOCAL_VAR; /* Can't seek (pread) on ttys. */ if (ppos != &file->f_pos) @@ -683,6 +685,7 @@ return -ERESTARTSYS; } #endif + SWSUSP_ACTIVITY_PAUSING; /* read_chan or equiv shouldn't stop a suspend */ lock_kernel(); if (tty->ldisc.read) i = (tty->ldisc.read)(tty,file,buf,count); @@ -691,6 +694,7 @@ unlock_kernel(); if (i > 0) inode->i_atime = CURRENT_TIME; + SWSUSP_ACTIVITY_RESTARTING(0); return i; } @@ -706,13 +710,18 @@ size_t count) { ssize_t ret = 0, written = 0; + DECLARE_SWSUSP_LOCAL_VAR; if (file->f_flags & O_NONBLOCK) { if (down_trylock(&tty->atomic_write)) return -EAGAIN; } else { - if (down_interruptible(&tty->atomic_write)) + int result; + SWSUSP_ACTIVITY_PAUSING; + result = down_interruptible(&tty->atomic_write); + SWSUSP_ACTIVITY_RESTARTING(0); + if (result) return -ERESTARTSYS; } if ( test_bit(TTY_NO_WRITE_SPLIT, &tty->flags) ) { diff -ruN linux-2.4.24/drivers/hotplug/cpqphp_ctrl.c software-suspend-linux-2.4.24-rev7/drivers/hotplug/cpqphp_ctrl.c --- linux-2.4.24/drivers/hotplug/cpqphp_ctrl.c 2004-01-22 19:46:34.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/drivers/hotplug/cpqphp_ctrl.c 2004-01-30 15:23:38.000000000 +1300 @@ -36,6 +36,7 @@ #include #include #include +#include #include "cpqphp.h" static u32 configure_new_device(struct controller* ctrl, struct pci_func *func,u8 behind_bridge, struct resource_lists *resources); @@ -1720,18 +1721,25 @@ static int event_thread(void* data) { struct controller *ctrl; + DECLARE_SWSUSP_LOCAL_VAR; + lock_kernel(); daemonize(); reparent_to_init(); // New name strcpy(current->comm, "phpd_event"); + + SWSUSP_THREAD_FLAGS_RESET; + SWSUSP_ACTIVITY_START(PF_SYNCTHREAD); unlock_kernel(); while (1) { dbg("!!!!event_thread sleeping\n"); + SWSUSP_ACTIVITY_PAUSING; down_interruptible (&event_semaphore); + SWSUSP_ACTIVITY_RESTARTING(PF_SYNCTHREAD); dbg("event_thread woken finished = %d\n", event_finished); if (event_finished) break; /* Do stuff here */ @@ -1742,6 +1750,7 @@ interrupt_event_handler(ctrl); } dbg("event_thread signals exit\n"); + SWSUSP_ACTIVITY_END; up(&event_exit); return 0; } diff -ruN linux-2.4.24/drivers/hotplug/ibmphp_hpc.c software-suspend-linux-2.4.24-rev7/drivers/hotplug/ibmphp_hpc.c --- linux-2.4.24/drivers/hotplug/ibmphp_hpc.c 2004-01-22 19:48:59.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/drivers/hotplug/ibmphp_hpc.c 2004-01-30 15:23:38.000000000 +1300 @@ -33,6 +33,7 @@ #include #include #include +#include #include "ibmphp.h" static int to_debug = FALSE; @@ -828,9 +829,13 @@ u8 curlatchlow = 0x00; int poll_count = 0; u8 ctrl_count = 0x00; + DECLARE_SWSUSP_LOCAL_VAR; debug ("%s - Entry\n", __FUNCTION__); + SWSUSP_THREAD_FLAGS_RESET; + SWSUSP_ACTIVITY_START(PF_SYNCTHREAD); + while (!ibmphp_shutdown) { if (ibmphp_shutdown) break; @@ -892,7 +897,9 @@ case POLL_SLEEP: /* don't sleep with a lock on the hardware */ up (&semOperations); + SWSUSP_ACTIVITY_PAUSING; long_delay (POLL_INTERVAL_SEC * HZ); + SWSUSP_ACTIVITY_RESTARTING(PF_SYNCTHREAD); if (ibmphp_shutdown) break; @@ -908,11 +915,14 @@ } /* give up the harware semaphore */ up (&semOperations); + SWSUSP_ACTIVITY_PAUSING; /* sleep for a short time just for good measure */ set_current_state (TASK_INTERRUPTIBLE); schedule_timeout (HZ/10); + SWSUSP_ACTIVITY_RESTARTING(PF_SYNCTHREAD); } up (&sem_exit); + SWSUSP_ACTIVITY_END; debug ("%s - Exit\n", __FUNCTION__); } diff -ruN linux-2.4.24/drivers/ide/ide-disk.c software-suspend-linux-2.4.24-rev7/drivers/ide/ide-disk.c --- linux-2.4.24/drivers/ide/ide-disk.c 2004-01-22 19:45:37.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/drivers/ide/ide-disk.c 2004-01-30 15:23:38.000000000 +1300 @@ -60,6 +60,7 @@ #include #include #include +#include #define _IDE_DISK @@ -1875,64 +1876,84 @@ return ide_stopped; } -int ide_disks_busy(void) +int ide_disks_busy(int no_warning) { int i; - for (i=0; ihandler) && (hwgroup->handler != panic_box)) - return 1; - } + + for (i=0; ihandler) && (hwgroup->handler != panic_box)) { + if(!no_warning) /* busy ide disks is not an error */ + printk("ide_disks_busy: %6s: handler not null: %p\n", + ide_hwifs[i].name,hwgroup->handler); + return 1; + } + } return 0; } void ide_disk_suspend(void) { - int i; - while (ide_disks_busy()) { - printk("*"); + int i, drivenum = 0; + ide_drive_t *drive; + + if (driver_blocked) + return; + + i=1; + while (ide_disks_busy(i++)) { schedule(); + i &= (unsigned int)((1<<21)-1); /* CBD: we print a warning only after a great number of errors, no need to frighten the newbie ;-) */ } - for (i=0; ihandler_save = hwgroup->handler; - hwgroup->handler = panic_box; + + /* Ensure caches are purged too (derived from module unload) - Nigel Cunningham */ + while ((drive = ide_scan_devices(ide_disk, idedisk_driver.name, + &idedisk_driver, drivenum)) != NULL) { + if ((drive->id->command_set_1 & 0x20) && drive->id->cfs_enable_1 & 0x20) { + if (do_idedisk_flushcache(drive)) + printk(KERN_ERR "Failed!"); + } + drivenum++; } + driver_blocked = 1; - if (ide_disks_busy()) + + for (i=0; ihandler = panic_box; + } +#if 1 + if (ide_disks_busy(0)) panic("How did you get that request through?!"); +#endif } -/* unsuspend and resume should be equal in the ideal world */ - -void ide_disk_unsuspend(void) +void ide_disk_unsuspend(int resume) { int i; - for (i=0; ihandler = NULL; /* hwgroup->handler_save; */ - hwgroup->handler_save = NULL; - } - driver_blocked = 0; -} - -void ide_disk_resume(void) -{ - int i; - for (i=0; ihandler != panic_box) - panic("Handler was not set to panic?"); - hwgroup->handler_save = NULL; - hwgroup->handler = NULL; - } + for (i=0; ihandler != panic_box) + printk(KERN_ERR "ide_disk_unsuspend: %6s: Handler was not set to panic? %p", + ide_hwifs[i].name,hwgroup->handler); + hwgroup->handler = NULL; + } driver_blocked = 0; } diff -ruN linux-2.4.24/drivers/ieee1394/nodemgr.c software-suspend-linux-2.4.24-rev7/drivers/ieee1394/nodemgr.c --- linux-2.4.24/drivers/ieee1394/nodemgr.c 2004-01-22 19:48:48.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/drivers/ieee1394/nodemgr.c 2004-01-30 15:23:38.000000000 +1300 @@ -20,6 +20,7 @@ #ifdef CONFIG_PROC_FS #include #endif +#include #include "ieee1394_types.h" #include "ieee1394.h" @@ -1294,11 +1295,14 @@ struct host_info *hi = (struct host_info *)__hi; struct hpsb_host *host = hi->host; int reset_cycles = 0; + DECLARE_SWSUSP_LOCAL_VAR; /* No userlevel access needed */ daemonize(); strcpy(current->comm, hi->daemon_name); + + SWSUSP_THREAD_FLAGS_RESET; /* Sit and wait for a signal to probe the nodes on the bus. This * happens when we get a bus reset. */ @@ -1312,8 +1316,12 @@ for (i = 0; i < 4 ; i++) { set_current_state(TASK_INTERRUPTIBLE); if (schedule_timeout(HZ/16)) { - up(&nodemgr_serialize); - goto caught_signal; + SWSUSP_ACTIVITY_START(PF_SYNCTHREAD); + SWSUSP_ACTIVITY_END; + if (signal_pending(current)) { + up(&nodemgr_serialize); + goto caught_signal; + } } /* Now get the generation in which the node ID's we collect @@ -1337,10 +1345,12 @@ reset_cycles = 0; + SWSUSP_ACTIVITY_START(PF_SYNCTHREAD); nodemgr_node_probe(hi, generation); nodemgr_do_irm_duties(host); up(&nodemgr_serialize); + SWSUSP_ACTIVITY_END; } caught_signal: diff -ruN linux-2.4.24/drivers/md/md.c software-suspend-linux-2.4.24-rev7/drivers/md/md.c --- linux-2.4.24/drivers/md/md.c 2004-01-22 19:46:18.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/drivers/md/md.c 2004-01-30 15:23:38.000000000 +1300 @@ -3588,6 +3588,8 @@ mdp_disk_t *spare; struct md_list_head *tmp; + current->flags |= PF_NOFREEZE; + printk(KERN_INFO "md: recovery thread got woken up ...\n"); restart: ITERATE_MDDEV(mddev,tmp) { @@ -3797,6 +3799,15 @@ detected_devices[dev_cnt++] = dev; } +void md_autostart_arrays(void) +{ + struct md_list_head *tmp; + mddev_t *mddev; + + autostart_arrays(); + ITERATE_MDDEV(mddev, tmp) + restart_array(mddev); +} static void autostart_arrays(void) { @@ -4127,4 +4138,5 @@ MD_EXPORT_SYMBOL(mddev_map); MD_EXPORT_SYMBOL(md_check_ordering); MD_EXPORT_SYMBOL(get_spare); +MD_EXPORT_SYMBOL(md_autostart_arrays); MODULE_LICENSE("GPL"); diff -ruN linux-2.4.24/drivers/md/raid1.c software-suspend-linux-2.4.24-rev7/drivers/md/raid1.c --- linux-2.4.24/drivers/md/raid1.c 2004-01-22 19:45:37.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/drivers/md/raid1.c 2004-01-30 15:23:38.000000000 +1300 @@ -1162,6 +1162,7 @@ if (mddev->sb_dirty) md_update_sb(mddev); + current->flags |= PF_NOFREEZE; for (;;) { md_spin_lock_irqsave(&retry_list_lock, flags); @@ -1286,6 +1287,8 @@ raid1_conf_t *conf = data; mddev_t *mddev = conf->mddev; + current->flags |= PF_NOFREEZE; + if (!conf->resync_mirrors) return; if (conf->resync_mirrors == 2) diff -ruN linux-2.4.24/drivers/md/raid5.c software-suspend-linux-2.4.24-rev7/drivers/md/raid5.c --- linux-2.4.24/drivers/md/raid5.c 2004-01-22 19:46:04.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/drivers/md/raid5.c 2004-01-30 15:23:38.000000000 +1300 @@ -1299,6 +1299,8 @@ PRINTK("+++ raid5d active\n"); + current->flags |= PF_NOFREEZE; + handled = 0; if (mddev->sb_dirty) @@ -1348,6 +1350,8 @@ raid5_conf_t *conf = data; mddev_t *mddev = conf->mddev; + current->flags |= PF_NOFREEZE; + if (!conf->resync_parity) return; if (conf->resync_parity == 2) diff -ruN linux-2.4.24/drivers/media/video/msp3400.c software-suspend-linux-2.4.24-rev7/drivers/media/video/msp3400.c --- linux-2.4.24/drivers/media/video/msp3400.c 2004-01-22 19:48:49.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/drivers/media/video/msp3400.c 2004-01-30 15:23:39.000000000 +1300 @@ -47,6 +47,7 @@ #include #include #include +#include #include #include @@ -771,6 +772,7 @@ struct CARRIER_DETECT *cd; int count, max1,max2,val1,val2, val,this; + DECLARE_SWSUSP_LOCAL_VAR; lock_kernel(); daemonize(); @@ -783,12 +785,17 @@ if(msp->notify != NULL) up(msp->notify); + SWSUSP_THREAD_FLAGS_RESET; + SWSUSP_ACTIVITY_START(PF_SYNCTHREAD); + for (;;) { if (msp->rmmod) goto done; if (debug > 1) printk("msp3400: thread: sleep\n"); + SWSUSP_ACTIVITY_PAUSING; interruptible_sleep_on(&msp->wq); + SWSUSP_ACTIVITY_RESTARTING(PF_SYNCTHREAD); if (debug > 1) printk("msp3400: thread: wakeup\n"); if (msp->rmmod || signal_pending(current)) @@ -804,7 +811,9 @@ /* some time for the tuner to sync */ current->state = TASK_INTERRUPTIBLE; + SWSUSP_ACTIVITY_PAUSING; schedule_timeout(HZ/5); + SWSUSP_ACTIVITY_RESTARTING(PF_SYNCTHREAD); if (signal_pending(current)) goto done; @@ -839,7 +848,9 @@ msp3400c_setcarrier(client, cd[this].cdo,cd[this].cdo); current->state = TASK_INTERRUPTIBLE; + SWSUSP_ACTIVITY_PAUSING; schedule_timeout(HZ/10); + SWSUSP_ACTIVITY_RESTARTING(PF_SYNCTHREAD); if (signal_pending(current)) goto done; if (msp->restart) @@ -876,7 +887,9 @@ msp3400c_setcarrier(client, cd[this].cdo,cd[this].cdo); current->state = TASK_INTERRUPTIBLE; + SWSUSP_ACTIVITY_PAUSING; schedule_timeout(HZ/10); + SWSUSP_ACTIVITY_RESTARTING(PF_SYNCTHREAD); if (signal_pending(current)) goto done; if (msp->restart) @@ -976,6 +989,7 @@ } done: + SWSUSP_ACTIVITY_END; dprintk(KERN_DEBUG "msp3400: thread: exit\n"); msp->active = 0; msp->thread = NULL; diff -ruN linux-2.4.24/drivers/media/video/tvaudio.c software-suspend-linux-2.4.24-rev7/drivers/media/video/tvaudio.c --- linux-2.4.24/drivers/media/video/tvaudio.c 2004-01-22 19:46:23.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/drivers/media/video/tvaudio.c 2004-01-30 15:23:39.000000000 +1300 @@ -28,6 +28,7 @@ #include #include #include +#include #include "audiochip.h" #include "id.h" @@ -272,6 +273,7 @@ { struct CHIPSTATE *chip = data; struct CHIPDESC *desc = chiplist + chip->type; + DECLARE_SWSUSP_LOCAL_VAR; lock_kernel(); daemonize(); @@ -284,8 +286,13 @@ if(chip->notify != NULL) up(chip->notify); + SWSUSP_THREAD_FLAGS_RESET; + SWSUSP_ACTIVITY_START(PF_SYNCTHREAD); + for (;;) { + SWSUSP_ACTIVITY_PAUSING; interruptible_sleep_on(&chip->wq); + SWSUSP_ACTIVITY_RESTARTING(PF_SYNCTHREAD); dprintk("%s: thread wakeup\n", i2c_clientname(&chip->c)); if (chip->done || signal_pending(current)) break; @@ -301,6 +308,7 @@ mod_timer(&chip->wt, jiffies+2*HZ); } + SWSUSP_ACTIVITY_END; chip->thread = NULL; dprintk("%s: thread exiting\n", i2c_clientname(&chip->c)); if(chip->notify != NULL) diff -ruN linux-2.4.24/drivers/message/i2o/i2o_block.c software-suspend-linux-2.4.24-rev7/drivers/message/i2o/i2o_block.c --- linux-2.4.24/drivers/message/i2o/i2o_block.c 2004-01-22 19:49:24.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/drivers/message/i2o/i2o_block.c 2004-01-30 15:23:39.000000000 +1300 @@ -65,6 +65,7 @@ #include #include +#include #include #include @@ -703,6 +704,7 @@ u16 pad; u8 data[16]; } *evt_local; + DECLARE_SWSUSP_LOCAL_VAR; lock_kernel(); daemonize(); @@ -710,16 +712,24 @@ strcpy(current->comm, "i2oblock"); evt_running = 1; + + SWSUSP_THREAD_FLAGS_RESET; + SWSUSP_ACTIVITY_START(PF_SYNCTHREAD); while(1) { + SWSUSP_ACTIVITY_PAUSING; + if(down_interruptible(&i2ob_evt_sem)) { + SWSUSP_ACTIVITY_RESTARTING(PF_SYNCTHREAD); evt_running = 0; printk("exiting..."); break; } + SWSUSP_ACTIVITY_RESTARTING(PF_SYNCTHREAD); + /* * Keep another CPU/interrupt from overwriting the * message while we're reading it @@ -848,6 +858,7 @@ } }; + SWSUSP_ACTIVITY_END; complete_and_exit(&i2ob_thread_dead,0); return 0; } diff -ruN linux-2.4.24/drivers/message/i2o/i2o_core.c software-suspend-linux-2.4.24-rev7/drivers/message/i2o/i2o_core.c --- linux-2.4.24/drivers/message/i2o/i2o_core.c 2004-01-22 19:48:57.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/drivers/message/i2o/i2o_core.c 2004-01-30 15:23:39.000000000 +1300 @@ -48,6 +48,8 @@ #include #include +#include + #include "i2o_lan.h" //#define DRIVERDEBUG @@ -872,24 +874,34 @@ u32 *msg = reply->msg; struct i2o_controller *c = NULL; unsigned long flags; + DECLARE_SWSUSP_LOCAL_VAR; lock_kernel(); daemonize(); unlock_kernel(); strcpy(current->comm, "i2oevtd"); + + SWSUSP_THREAD_FLAGS_RESET; + SWSUSP_ACTIVITY_START(PF_SYNCTHREAD); evt_running = 1; while(1) { + SWSUSP_ACTIVITY_PAUSING; + if(down_interruptible(&evt_sem)) { + SWSUSP_ACTIVITY_RESTARTING(PF_SYNCTHREAD); + SWSUSP_ACTIVITY_END; dprintk(KERN_INFO "I2O event thread dead\n"); printk("exiting..."); evt_running = 0; complete_and_exit(&evt_dead, 0); } + SWSUSP_ACTIVITY_RESTARTING(PF_SYNCTHREAD); + /* * Copy the data out of the queue so that we don't have to lock * around the whole function and just around the qlen update @@ -1021,6 +1033,7 @@ } } + SWSUSP_ACTIVITY_END; return 0; } @@ -1042,6 +1055,7 @@ int entries; void *tmp; char name[16]; + DECLARE_SWSUSP_LOCAL_VAR; lock_kernel(); daemonize(); @@ -1049,16 +1063,25 @@ sprintf(name, "iop%d_lctd", c->unit); strcpy(current->comm, name); + + SWSUSP_THREAD_FLAGS_RESET; + SWSUSP_ACTIVITY_START(PF_SYNCTHREAD); c->lct_running = 1; while(1) { + SWSUSP_ACTIVITY_PAUSING; + down_interruptible(&c->lct_sem); + + SWSUSP_ACTIVITY_RESTARTING(PF_SYNCTHREAD); + if(signal_pending(current)) { dprintk(KERN_ERR "%s: LCT thread dead\n", c->name); c->lct_running = 0; + SWSUSP_ACTIVITY_END; return 0; } @@ -1134,6 +1157,7 @@ memcpy(c->lct, c->dlct, c->dlct->table_size<<2); } + SWSUSP_ACTIVITY_END; return 0; } diff -ruN linux-2.4.24/drivers/mtd/mtdblock.c software-suspend-linux-2.4.24-rev7/drivers/mtd/mtdblock.c --- linux-2.4.24/drivers/mtd/mtdblock.c 2004-01-22 19:46:49.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/drivers/mtd/mtdblock.c 2004-01-30 15:23:39.000000000 +1300 @@ -13,6 +13,7 @@ #include #include #include +#include #define MAJOR_NR MTD_BLOCK_MAJOR #define DEVICE_NAME "mtdblock" @@ -483,6 +484,7 @@ { struct task_struct *tsk = current; DECLARE_WAITQUEUE(wait, tsk); + DECLARE_SWSUSP_LOCAL_VAR; /* we might get involved when memory gets low, so use PF_MEMALLOC */ tsk->flags |= PF_MEMALLOC; @@ -492,6 +494,10 @@ recalc_sigpending(tsk); spin_unlock_irq(&tsk->sigmask_lock); daemonize(); + current->flags |= PF_SYNCTHREAD; + + SWSUSP_THREAD_FLAGS_RESET; + SWSUSP_ACTIVITY_START(PF_SYNCTHREAD); while (!leaving) { add_wait_queue(&thr_wq, &wait); @@ -499,8 +505,10 @@ spin_lock_irq(&io_request_lock); if (QUEUE_EMPTY || QUEUE_PLUGGED) { spin_unlock_irq(&io_request_lock); + SWSUSP_ACTIVITY_SYNCTHREAD_PAUSING; schedule(); remove_wait_queue(&thr_wq, &wait); + SWSUSP_ACTIVITY_RESTARTING(PF_SYNCTHREAD); } else { remove_wait_queue(&thr_wq, &wait); set_current_state(TASK_RUNNING); @@ -509,6 +517,7 @@ } } + SWSUSP_ACTIVITY_END; up(&thread_sem); return 0; } diff -ruN linux-2.4.24/drivers/net/8139too.c software-suspend-linux-2.4.24-rev7/drivers/net/8139too.c --- linux-2.4.24/drivers/net/8139too.c 2004-01-22 19:45:34.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/drivers/net/8139too.c 2004-01-30 15:23:39.000000000 +1300 @@ -110,6 +110,7 @@ #include #include #include +#include #include #include @@ -1596,6 +1597,8 @@ while (1) { timeout = next_tick; do { + if (current->flags & PF_FREEZE) + refrigerator(PF_SYNCTHREAD); timeout = interruptible_sleep_on_timeout (&tp->thr_wait, timeout); } while (!signal_pending (current) && (timeout > 0)); diff -ruN linux-2.4.24/drivers/net/eepro100.c software-suspend-linux-2.4.24-rev7/drivers/net/eepro100.c --- linux-2.4.24/drivers/net/eepro100.c 2004-01-22 19:49:05.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/drivers/net/eepro100.c 2004-01-30 15:23:39.000000000 +1300 @@ -529,6 +529,8 @@ static int eepro100_init_one(struct pci_dev *pdev, const struct pci_device_id *ent); static void eepro100_remove_one (struct pci_dev *pdev); +static int eepro100_suspend (struct pci_dev *pdev, u32 state); +static int eepro100_resume (struct pci_dev *pdev); static int do_eeprom_cmd(long ioaddr, int cmd, int cmd_len); static int mdio_read(struct net_device *dev, int phy_id, int location); diff -ruN linux-2.4.24/drivers/net/via-rhine.c software-suspend-linux-2.4.24-rev7/drivers/net/via-rhine.c --- linux-2.4.24/drivers/net/via-rhine.c 2004-01-22 19:47:19.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/drivers/net/via-rhine.c 2004-01-30 15:23:39.000000000 +1300 @@ -1891,12 +1891,40 @@ pci_set_drvdata(pdev, NULL); } +#ifdef CONFIG_PM +static int via_rhine_suspend(struct pci_dev *pdev, u32 state) +{ + printk("Suspending via_rhine...\n"); +#if 0 + via_rhine_remove_one(pdev); +#endif + return 0; +} + +static int via_rhine_resume(struct pci_dev *pdev) +{ + long ioaddr; + + printk("Resuming via_rhine...\n"); +#if 1 + ioaddr = pci_resource_start (pdev, 0); + /* Reset the chip. */ + writew(CmdReset, ioaddr + ChipCmd); +#endif + return 0; +} +#endif + static struct pci_driver via_rhine_driver = { .name = "via-rhine", .id_table = via_rhine_pci_tbl, .probe = via_rhine_init_one, .remove = __devexit_p(via_rhine_remove_one), +#ifdef CONFIG_PM + .suspend = via_rhine_suspend, + .resume = via_rhine_resume +#endif }; diff -ruN linux-2.4.24/drivers/parport/ieee1284.c software-suspend-linux-2.4.24-rev7/drivers/parport/ieee1284.c --- linux-2.4.24/drivers/parport/ieee1284.c 2004-01-22 19:45:49.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/drivers/parport/ieee1284.c 2004-01-30 15:23:39.000000000 +1300 @@ -22,6 +22,7 @@ #include #include #include +#include #undef DEBUG /* undef me for production */ @@ -68,6 +69,7 @@ { int ret; struct timer_list timer; + DECLARE_SWSUSP_LOCAL_VAR; if (!port->physport->cad->timeout) /* Zero timeout is special, and we can't down() the @@ -81,7 +83,9 @@ timer.data = port->number; add_timer (&timer); + SWSUSP_ACTIVITY_PAUSING; ret = down_interruptible (&port->physport->ieee1284.irq); + SWSUSP_ACTIVITY_RESTARTING(0); if (!del_timer (&timer) && !ret) /* Timed out. */ ret = 1; diff -ruN linux-2.4.24/drivers/scsi/scsi_error.c software-suspend-linux-2.4.24-rev7/drivers/scsi/scsi_error.c --- linux-2.4.24/drivers/scsi/scsi_error.c 2004-01-22 19:45:38.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/drivers/scsi/scsi_error.c 2004-01-30 15:23:39.000000000 +1300 @@ -22,6 +22,7 @@ #include #include #include +#include #define __KERNEL_SYSCALLS__ @@ -1875,6 +1876,7 @@ */ sprintf(current->comm, "scsi_eh_%d", host->host_no); + current->flags |= PF_NOFREEZE; host->eh_wait = &sem; host->ehandler = current; @@ -1906,6 +1908,12 @@ * semaphores isn't unreasonable. */ down_interruptible(&sem); + /* + * FIXME: Does Software Suspend need to do more for this + * thread? Should we skip back to the top? If you use + * this module, please contact the software suspend + * developers and let us know if it works as is. + */ if( host->loaded_as_module ) { if (signal_pending(current)) break; diff -ruN linux-2.4.24/drivers/usb/host/usb-ohci.c software-suspend-linux-2.4.24-rev7/drivers/usb/host/usb-ohci.c --- linux-2.4.24/drivers/usb/host/usb-ohci.c 2004-01-22 19:46:08.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/drivers/usb/host/usb-ohci.c 2004-01-30 15:23:39.000000000 +1300 @@ -65,9 +65,11 @@ #include #include #include +#include #include /* for in_interrupt() */ #undef DEBUG #include +#include #include #include @@ -107,6 +109,9 @@ static LIST_HEAD (ohci_hcd_list); static spinlock_t usb_ed_lock = SPIN_LOCK_UNLOCKED; +#ifdef CONFIG_SOFTWARE_SUSPEND2 +static ohci_t *ohci_save = NULL; +#endif /* CONFIG_SOFTWARE_SUSPEND2 */ /*-------------------------------------------------------------------------*/ @@ -2580,6 +2585,14 @@ int temp; int i; +#ifdef CONFIG_SOFTWARE_SUSPEND2 + /* dont do anything till we're out of a software suspend */ + if (suspend_task) { + ohci_save = ohci; + return; + } +#endif /* CONFIG_SOFTWARE_SUSPEND2 */ + if (ohci->pci_latency) pci_write_config_byte (ohci->ohci_dev, PCI_LATENCY_TIMER, ohci->pci_latency); @@ -2938,23 +2951,49 @@ }; +#ifdef CONFIG_SOFTWARE_SUSPEND2 +static struct notifier_block ohci_resume_notifier; +#endif /* CONFIG_SOFTWARE_SUSPEND2 */ + /*-------------------------------------------------------------------------*/ static int __init ohci_hcd_init (void) { - return pci_module_init (&ohci_pci_driver); + int retval = pci_module_init (&ohci_pci_driver); +#ifdef CONFIG_SOFTWARE_SUSPEND2 + if (!retval) + register_resume_notifier(&ohci_resume_notifier); +#endif /* CONFIG_SOFTWARE_SUSPEND2 */ + return retval; } /*-------------------------------------------------------------------------*/ static void __exit ohci_hcd_cleanup (void) { +#ifdef CONFIG_SOFTWARE_SUSPEND2 + unregister_resume_notifier(&ohci_resume_notifier); +#endif /* CONFIG_SOFTWARE_SUSPEND2 */ pci_unregister_driver (&ohci_pci_driver); } module_init (ohci_hcd_init); module_exit (ohci_hcd_cleanup); +#ifdef CONFIG_SOFTWARE_SUSPEND2 +static int ohci_swsusp_resume(struct notifier_block *self, unsigned long v, void *vp) +{ + if (ohci_save) { + hc_restart(ohci_save); + ohci_save = NULL; + } + return 0; +} + +static struct notifier_block ohci_resume_notifier = { + .notifier_call = ohci_swsusp_resume, +}; +#endif /* CONFIG_SOFTWARE_SUSPEND2 */ MODULE_AUTHOR( DRIVER_AUTHOR ); MODULE_DESCRIPTION( DRIVER_DESC ); diff -ruN linux-2.4.24/drivers/usb/hub.c software-suspend-linux-2.4.24-rev7/drivers/usb/hub.c --- linux-2.4.24/drivers/usb/hub.c 2004-01-22 19:47:34.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/drivers/usb/hub.c 2004-01-30 15:23:39.000000000 +1300 @@ -16,6 +16,7 @@ #include #include #include +#include #ifdef CONFIG_USB_DEBUG #define DEBUG #else @@ -902,6 +903,7 @@ static int usb_hub_thread(void *__hub) { + DECLARE_SWSUSP_LOCAL_VAR; lock_kernel(); /* @@ -914,13 +916,20 @@ /* Setup a nice name */ strcpy(current->comm, "khubd"); + current->flags |= PF_SYNCTHREAD; + + SWSUSP_THREAD_FLAGS_RESET; + SWSUSP_ACTIVITY_START(PF_SYNCTHREAD); /* Send me a signal to get me die (for debugging) */ do { usb_hub_events(); + SWSUSP_ACTIVITY_SYNCTHREAD_PAUSING; wait_event_interruptible(khubd_wait, !list_empty(&hub_event_list)); + SWSUSP_ACTIVITY_RESTARTING(PF_SYNCTHREAD); } while (!signal_pending(current)); + SWSUSP_ACTIVITY_END; dbg("usb_hub_thread exiting"); unlock_kernel(); diff -ruN linux-2.4.24/drivers/usb/storage/usb.c software-suspend-linux-2.4.24-rev7/drivers/usb/storage/usb.c --- linux-2.4.24/drivers/usb/storage/usb.c 2004-01-22 19:46:10.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/drivers/usb/storage/usb.c 2004-01-30 15:23:39.000000000 +1300 @@ -47,6 +47,7 @@ */ #include +#include #include "usb.h" #include "scsiglue.h" #include "transport.h" @@ -310,6 +311,7 @@ { struct us_data *us = (struct us_data *)__us; int action; + DECLARE_SWSUSP_LOCAL_VAR; lock_kernel(); @@ -332,6 +334,10 @@ /* set our name for identification purposes */ sprintf(current->comm, "usb-storage-%d", us->host_number); + current->flags |= PF_SYNCTHREAD; + + SWSUSP_THREAD_FLAGS_RESET; + SWSUSP_ACTIVITY_START(PF_SYNCTHREAD); unlock_kernel(); @@ -345,10 +351,14 @@ for(;;) { unsigned long flags; US_DEBUGP("*** thread sleeping.\n"); - if(down_interruptible(&us->sema)) + SWSUSP_ACTIVITY_SYNCTHREAD_PAUSING; + if(down_interruptible(&us->sema)) { + SWSUSP_ACTIVITY_RESTARTING(PF_SYNCTHREAD); break; + } US_DEBUGP("*** thread awakened.\n"); + SWSUSP_ACTIVITY_RESTARTING(PF_SYNCTHREAD); /* lock access to the queue element */ spin_lock_irqsave(&(us->queue_exclusion), flags); @@ -488,6 +498,8 @@ /* clean up after ourselves */ set_current_state(TASK_INTERRUPTIBLE); + SWSUSP_ACTIVITY_END; + /* notify the exit routine that we're actually exiting now */ complete(&(us->notify)); diff -ruN linux-2.4.24/fs/buffer.c software-suspend-linux-2.4.24-rev7/fs/buffer.c --- linux-2.4.24/fs/buffer.c 2004-01-22 19:48:34.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/fs/buffer.c 2004-01-30 15:23:39.000000000 +1300 @@ -47,6 +47,8 @@ #include #include #include +#include +#include #include #include @@ -76,7 +78,7 @@ static spinlock_cacheline_t lru_list_lock_cacheline = {SPIN_LOCK_UNLOCKED}; #define lru_list_lock lru_list_lock_cacheline.lock -static int nr_buffers_type[NR_LIST]; +int nr_buffers_type[NR_LIST]; static unsigned long size_buffers_type[NR_LIST]; static struct buffer_head * unused_list; @@ -341,16 +343,21 @@ kdev_t dev = sb->s_dev; sync_buffers(dev, 0); - lock_kernel(); - sync_inodes_sb(sb); - DQUOT_SYNC_SB(sb); - lock_super(sb); - if (sb->s_dirt && sb->s_op && sb->s_op->write_super) - sb->s_op->write_super(sb); - unlock_super(sb); - if (sb->s_op && sb->s_op->sync_fs) - sb->s_op->sync_fs(sb); - unlock_kernel(); +#ifdef CONFIG_SOFTWARE_SUSPEND2 + if (likely(!(swsusp_state & FREEZE_UNREFRIGERATED))) +#endif + { + lock_kernel(); + sync_inodes_sb(sb); + DQUOT_SYNC_SB(sb); + lock_super(sb); + if (sb->s_dirt && sb->s_op && sb->s_op->write_super) + sb->s_op->write_super(sb); + unlock_super(sb); + if (sb->s_op && sb->s_op->sync_fs) + sb->s_op->sync_fs(sb); + unlock_kernel(); + } return sync_buffers(dev, 1); } @@ -365,11 +372,16 @@ { sync_buffers(dev, 0); - lock_kernel(); - sync_inodes(dev); - DQUOT_SYNC_DEV(dev); - sync_supers(dev, 1); - unlock_kernel(); +#ifdef CONFIG_SOFTWARE_SUSPEND2 + if (likely(!(swsusp_state & FREEZE_UNREFRIGERATED))) +#endif + { + lock_kernel(); + sync_inodes(dev); + DQUOT_SYNC_DEV(dev); + sync_supers(dev, 1); + unlock_kernel(); + } return sync_buffers(dev, 1); } @@ -383,9 +395,22 @@ fsync_dev(dev); } +/* + * When trying to freeze processes (for Software Suspend), + * we want to put new attempts at syncing on hold until + * resume time (yes, they'll be redundant then, but that + * doesn't matter) and we want to wait for current attempts + * to finish. + */ asmlinkage long sys_sync(void) { + DECLARE_SWSUSP_LOCAL_VAR; + + SWSUSP_ACTIVITY_START(0); + current->flags |= PF_SYNCTHREAD; fsync_dev(0); + current->flags &= ~PF_SYNCTHREAD; + SWSUSP_ACTIVITY_END; return 0; } @@ -424,6 +449,10 @@ struct dentry * dentry; struct inode * inode; int ret, err; + DECLARE_SWSUSP_LOCAL_VAR; + + SWSUSP_ACTIVITY_START(0); + current->flags |= PF_SYNCTHREAD; ret = -EBADF; file = fget(fd); @@ -453,6 +482,8 @@ out_putf: fput(file); out: + current->flags &= ~PF_SYNCTHREAD; + SWSUSP_ACTIVITY_END; return ret; } @@ -483,6 +514,10 @@ struct file * file; struct inode *inode; int ret; + DECLARE_SWSUSP_LOCAL_VAR; + + SWSUSP_ACTIVITY_START(0); + current->flags |= PF_SYNCTHREAD; ret = -EBADF; file = fget(fd); @@ -496,6 +531,8 @@ fput(file); out: + current->flags &= ~PF_SYNCTHREAD; + SWSUSP_ACTIVITY_END; return ret; } @@ -753,7 +790,8 @@ { balance_dirty(); wakeup_bdflush(); - try_to_free_pages(GFP_NOIO); + if (likely(suspend_task != current->pid)) + try_to_free_pages(GFP_NOIO); run_task_queue(&tq_disk); yield(); } @@ -765,7 +803,7 @@ bh->b_private = private; } -static void end_buffer_io_async(struct buffer_head * bh, int uptodate) +void end_buffer_io_async(struct buffer_head * bh, int uptodate) { static spinlock_t page_uptodate_lock = SPIN_LOCK_UNLOCKED; unsigned long flags; @@ -1328,6 +1366,10 @@ run_task_queue(&tq_disk); free_more_memory(); +#ifdef CONFIG_SOFTWARE_SUSPEND2 + if (suspend_task == current->pid) + cleanup_finished_swsusp_io(); +#endif goto try_again; } @@ -2513,7 +2555,12 @@ goto failed; } - bh = create_buffers(page, size, 0); +#ifdef CONFIG_SOFTWARE_SUSPEND2 + if (suspend_task == current->pid) + bh = create_buffers(page, size, 1); + else +#endif + bh = create_buffers(page, size, 0); if (!bh) goto failed; link_dev_buffers(page, bh); @@ -2887,14 +2934,21 @@ * otherwise there would be no way of ensuring that these quantities ever * get written back. Ideally, we would have a timestamp on the inodes * and superblocks so that we could write back only the old ones as well + * + * Not static so it can be used during swsusp process freezing. */ -static int sync_old_buffers(void) +int sync_old_buffers(void) { - lock_kernel(); - sync_unlocked_inodes(); - sync_supers(0, 0); - unlock_kernel(); +#ifdef CONFIG_SOFTWARE_SUSPEND2 + if (likely(!(swsusp_state & FREEZE_UNREFRIGERATED))) +#endif + { + lock_kernel(); + sync_unlocked_inodes(); + sync_supers(0, 0); + unlock_kernel(); + } for (;;) { struct buffer_head *bh; @@ -3012,6 +3066,10 @@ */ for (;;) { int ndirty = bdf_prm.b_un.ndirty; + DECLARE_SWSUSP_LOCAL_VAR; + + SWSUSP_ACTIVITY_START(PF_SYNCTHREAD); + current->flags |= PF_SYNCTHREAD; CHECK_EMERGENCY_SYNC @@ -3021,6 +3079,9 @@ break; ndirty -= NRSYNC; } + current->flags &= ~PF_SYNCTHREAD; + SWSUSP_ACTIVITY_END; + if (ndirty > 0 || bdflush_stop()) interruptible_sleep_on(&bdflush_wait); } @@ -3052,6 +3113,7 @@ for (;;) { DECLARE_WAITQUEUE(wait, tsk); + DECLARE_SWSUSP_LOCAL_VAR; add_wait_queue(&kupdate_wait, &wait); @@ -3065,6 +3127,8 @@ schedule(); /* wait for SIGCONT */ } remove_wait_queue(&kupdate_wait, &wait); + SWSUSP_ACTIVITY_START(PF_SYNCTHREAD); + current->flags |= PF_SYNCTHREAD; /* check for sigstop */ if (signal_pending(tsk)) { int sig, stopped = 0; @@ -3087,6 +3151,8 @@ if (laptop_mode) fsync_dev(NODEV); run_task_queue(&tq_disk); + current->flags &= ~PF_SYNCTHREAD; + SWSUSP_ACTIVITY_END; } } diff -ruN linux-2.4.24/fs/dcache.c software-suspend-linux-2.4.24-rev7/fs/dcache.c --- linux-2.4.24/fs/dcache.c 2004-01-22 19:45:54.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/fs/dcache.c 2004-01-30 15:23:39.000000000 +1300 @@ -23,6 +23,7 @@ #include #include #include +#include #include @@ -1022,10 +1023,13 @@ struct vfsmount *pwdmnt, *rootmnt; struct dentry *pwd, *root; char *page = (char *) __get_free_page(GFP_USER); + DECLARE_SWSUSP_LOCAL_VAR; if (!page) return -ENOMEM; + SWSUSP_ACTIVITY_START(0); + read_lock(¤t->fs->lock); pwdmnt = mntget(current->fs->pwdmnt); pwd = dget(current->fs->pwd); @@ -1063,6 +1067,9 @@ dput(root); mntput(rootmnt); free_page((unsigned long) page); + + SWSUSP_ACTIVITY_END; + return error; } diff -ruN linux-2.4.24/fs/devfs/base.c software-suspend-linux-2.4.24-rev7/fs/devfs/base.c --- linux-2.4.24/fs/devfs/base.c 2004-01-22 19:45:59.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/fs/devfs/base.c 2004-01-30 15:23:39.000000000 +1300 @@ -652,6 +652,7 @@ #include #include #include +#include #include #include @@ -1446,14 +1447,17 @@ static int wait_for_devfsd_finished (struct fs_info *fs_info) { DECLARE_WAITQUEUE (wait, current); + DECLARE_SWSUSP_LOCAL_VAR; if (fs_info->devfsd_task == NULL) return (TRUE); if (devfsd_queue_empty (fs_info) && fs_info->devfsd_sleeping) return TRUE; if ( is_devfsd_or_child (fs_info) ) return (FALSE); + SWSUSP_ACTIVITY_PAUSING; set_current_state (TASK_UNINTERRUPTIBLE); add_wait_queue (&fs_info->revalidate_wait_queue, &wait); if (!devfsd_queue_empty (fs_info) || !fs_info->devfsd_sleeping) if (fs_info->devfsd_task) schedule (); + SWSUSP_ACTIVITY_RESTARTING(PF_SYNCTHREAD); remove_wait_queue (&fs_info->revalidate_wait_queue, &wait); __set_current_state (TASK_RUNNING); return (TRUE); @@ -2939,6 +2943,7 @@ devfs_handle_t parent = get_devfs_entry_from_vfs_inode (dir); struct devfs_lookup_struct *lookup_info = dentry->d_fsdata; DECLARE_WAITQUEUE (wait, current); + DECLARE_SWSUSP_LOCAL_VAR; if ( is_devfsd_or_child (fs_info) ) { @@ -2972,10 +2977,12 @@ read_lock (&parent->u.dir.lock); if (dentry->d_fsdata) { + SWSUSP_ACTIVITY_PAUSING; set_current_state (TASK_UNINTERRUPTIBLE); add_wait_queue (&lookup_info->wait_queue, &wait); read_unlock (&parent->u.dir.lock); schedule (); + SWSUSP_ACTIVITY_RESTARTING(PF_SYNCTHREAD); } else read_unlock (&parent->u.dir.lock); return 1; @@ -3318,11 +3325,13 @@ struct fs_info *fs_info = file->f_dentry->d_inode->i_sb->u.generic_sbp; struct devfsd_notify_struct *info = fs_info->devfsd_info; DECLARE_WAITQUEUE (wait, current); + DECLARE_SWSUSP_LOCAL_VAR; /* Can't seek (pread) on this device */ if (ppos != &file->f_pos) return -ESPIPE; /* Verify the task has grabbed the queue */ if (fs_info->devfsd_task != current) return -EPERM; + current->flags |= PF_SYNCTHREAD; info->major = 0; info->minor = 0; /* Block for a new entry */ @@ -3331,13 +3340,16 @@ while ( devfsd_queue_empty (fs_info) ) { fs_info->devfsd_sleeping = TRUE; + SWSUSP_ACTIVITY_SYNCTHREAD_PAUSING; wake_up (&fs_info->revalidate_wait_queue); schedule (); + SWSUSP_ACTIVITY_RESTARTING(PF_SYNCTHREAD); fs_info->devfsd_sleeping = FALSE; if ( signal_pending (current) ) { remove_wait_queue (&fs_info->devfsd_wait_queue, &wait); __set_current_state (TASK_RUNNING); + current->flags &= ~PF_SYNCTHREAD; return -EINTR; } set_current_state (TASK_INTERRUPTIBLE); @@ -3359,7 +3371,10 @@ info->minor = de->u.fcb.u.device.minor; } pos = devfs_generate_path (de, info->devname, DEVFS_PATHLEN); - if (pos < 0) return pos; + if (pos < 0) { + current->flags &= ~PF_SYNCTHREAD; + return pos; + } info->namelen = DEVFS_PATHLEN - pos - 1; if (info->mode == 0) info->mode = de->mode; devname_offset = info->devname - (char *) info; @@ -3371,6 +3386,7 @@ if (tlen > len) tlen = len; if ( copy_to_user (buf, (char *) info + rpos, tlen) ) { + current->flags &= ~PF_SYNCTHREAD; return -EFAULT; } rpos += tlen; @@ -3386,6 +3402,7 @@ if ( copy_to_user (buf, info->devname + pos + rpos - devname_offset, tlen) ) { + current->flags &= ~PF_SYNCTHREAD; return -EFAULT; } rpos += tlen; @@ -3409,6 +3426,7 @@ *ppos = 0; } else *ppos = rpos; + current->flags &= ~PF_SYNCTHREAD; return tlen; } /* End Function devfsd_read */ diff -ruN linux-2.4.24/fs/exec.c software-suspend-linux-2.4.24-rev7/fs/exec.c --- linux-2.4.24/fs/exec.c 2004-01-22 19:46:48.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/fs/exec.c 2004-01-30 15:23:39.000000000 +1300 @@ -39,6 +39,7 @@ #include #define __NO_VERSION__ #include +#include #include #include @@ -112,6 +113,9 @@ struct file * file; struct nameidata nd; int error; + DECLARE_SWSUSP_LOCAL_VAR; + + SWSUSP_ACTIVITY_START(0); error = user_path_walk(library, &nd); if (error) @@ -151,6 +155,7 @@ } fput(file); out: + SWSUSP_ACTIVITY_END; return error; exit: path_release(&nd); @@ -903,12 +908,17 @@ struct file *file; int retval; int i; + DECLARE_SWSUSP_LOCAL_VAR; + + SWSUSP_ACTIVITY_START(0); file = open_exec(filename); retval = PTR_ERR(file); - if (IS_ERR(file)) + if (IS_ERR(file)) { + SWSUSP_ACTIVITY_END; return retval; + } bprm.p = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *); memset(bprm.page, 0, MAX_ARG_PAGES*sizeof(bprm.page[0])); @@ -921,12 +931,14 @@ if ((bprm.argc = count(argv, bprm.p / sizeof(void *))) < 0) { allow_write_access(file); fput(file); + SWSUSP_ACTIVITY_END; return bprm.argc; } if ((bprm.envc = count(envp, bprm.p / sizeof(void *))) < 0) { allow_write_access(file); fput(file); + SWSUSP_ACTIVITY_END; return bprm.envc; } @@ -948,9 +960,11 @@ goto out; retval = search_binary_handler(&bprm,regs); - if (retval >= 0) + if (retval >= 0) { /* execve success */ + SWSUSP_ACTIVITY_END; return retval; + } out: /* Something went wrong, return the inode and free the argument pages*/ @@ -964,6 +978,7 @@ __free_page(page); } + SWSUSP_ACTIVITY_END; return retval; } @@ -1101,6 +1116,9 @@ struct inode * inode; int retval = 0; int fsuid = current->fsuid; + DECLARE_SWSUSP_LOCAL_VAR; + + SWSUSP_ACTIVITY_START(0); lock_kernel(); binfmt = current->binfmt; @@ -1143,5 +1161,7 @@ if (fsuid != current->fsuid) current->fsuid = fsuid; unlock_kernel(); + SWSUSP_ACTIVITY_END; + return retval; } diff -ruN linux-2.4.24/fs/fcntl.c software-suspend-linux-2.4.24-rev7/fs/fcntl.c --- linux-2.4.24/fs/fcntl.c 2004-01-22 19:45:50.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/fs/fcntl.c 2004-01-30 15:23:39.000000000 +1300 @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -141,6 +142,9 @@ int err = -EBADF; struct file * file, *tofree; struct files_struct * files = current->files; + DECLARE_SWSUSP_LOCAL_VAR; + + SWSUSP_ACTIVITY_START(0); write_lock(&files->file_lock); if (!(file = fcheck(oldfd))) @@ -179,6 +183,7 @@ filp_close(tofree, files); err = newfd; out: + SWSUSP_ACTIVITY_END; return err; out_unlock: write_unlock(&files->file_lock); @@ -194,9 +199,12 @@ { int ret = -EBADF; struct file * file = fget(fildes); + DECLARE_SWSUSP_LOCAL_VAR; + SWSUSP_ACTIVITY_START(0); if (file) ret = dupfd(file, 0); + SWSUSP_ACTIVITY_END; return ret; } @@ -341,7 +349,9 @@ { struct file * filp; long err = -EBADF; + DECLARE_SWSUSP_LOCAL_VAR; + SWSUSP_ACTIVITY_START(0); filp = fget(fd); if (!filp) goto out; @@ -350,6 +360,7 @@ fput(filp); out: + SWSUSP_ACTIVITY_END; return err; } @@ -358,7 +369,9 @@ { struct file * filp; long err; + DECLARE_SWSUSP_LOCAL_VAR; + SWSUSP_ACTIVITY_START(0); err = -EBADF; filp = fget(fd); if (!filp) @@ -380,6 +393,7 @@ } fput(filp); out: + SWSUSP_ACTIVITY_END; return err; } #endif diff -ruN linux-2.4.24/fs/jbd/journal.c software-suspend-linux-2.4.24-rev7/fs/jbd/journal.c --- linux-2.4.24/fs/jbd/journal.c 2004-01-22 19:45:59.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/fs/jbd/journal.c 2004-01-30 15:23:39.000000000 +1300 @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -199,6 +200,7 @@ journal_t *journal = (journal_t *) arg; transaction_t *transaction; struct timer_list timer; + DECLARE_SWSUSP_LOCAL_VAR; current_journal = journal; @@ -211,6 +213,10 @@ spin_unlock_irq(¤t->sigmask_lock); sprintf(current->comm, "kjournald"); + current->flags |= PF_SYNCTHREAD; + + SWSUSP_THREAD_FLAGS_RESET; + SWSUSP_ACTIVITY_START(PF_SYNCTHREAD); /* Set up an interval timer which can be used to trigger a commit wakeup after the commit interval expires */ @@ -247,7 +253,9 @@ } wake_up(&journal->j_wait_done_commit); + SWSUSP_ACTIVITY_SYNCTHREAD_PAUSING; interruptible_sleep_on(&journal->j_wait_commit); + SWSUSP_ACTIVITY_RESTARTING(PF_SYNCTHREAD); jbd_debug(1, "kjournald wakes\n"); @@ -264,6 +272,7 @@ del_timer_sync(journal->j_commit_timer); } + SWSUSP_ACTIVITY_END; list_del(&journal->j_all_journals); journal->j_task = NULL; diff -ruN linux-2.4.24/fs/jffs/intrep.c software-suspend-linux-2.4.24-rev7/fs/jffs/intrep.c --- linux-2.4.24/fs/jffs/intrep.c 2004-01-22 19:48:38.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/fs/jffs/intrep.c 2004-01-30 15:23:39.000000000 +1300 @@ -70,6 +70,7 @@ #include #include #include +#include #include "intrep.h" #include "jffs_fm.h" @@ -3340,7 +3341,14 @@ struct jffs_fmcontrol *fmc = c->fmc; long erased; int result = 0; - D1(int i = 1); +#if CONFIG_JFFS_FS_VERBOSE > 0 + /* + * For the swsusp macro that follows, we can't just use D1 + * with some compilers + */ + int i = 1; +#endif + DECLARE_SWSUSP_LOCAL_VAR; c->gc_task = current; @@ -3355,6 +3363,9 @@ recalc_sigpending(current); spin_unlock_irq(¤t->sigmask_lock); strcpy(current->comm, "jffs_gcd"); + current->flags |= PF_SYNCTHREAD; + + SWSUSP_THREAD_FLAGS_RESET; D1(printk (KERN_NOTICE "jffs_garbage_collect_thread(): Starting infinite loop.\n")); @@ -3399,6 +3410,7 @@ } } + SWSUSP_ACTIVITY_START(PF_SYNCTHREAD); D1(printk (KERN_NOTICE "jffs_garbage_collect_thread(): collecting.\n")); @@ -3435,5 +3447,6 @@ gc_end: D3(printk (KERN_NOTICE "g_c_thread(): up biglock\n")); up(&fmc->biglock); + SWSUSP_ACTIVITY_END; } /* for (;;) */ } /* jffs_garbage_collect_thread() */ diff -ruN linux-2.4.24/fs/jffs2/background.c software-suspend-linux-2.4.24-rev7/fs/jffs2/background.c --- linux-2.4.24/fs/jffs2/background.c 2004-01-22 19:45:38.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/fs/jffs2/background.c 2004-01-30 15:23:39.000000000 +1300 @@ -44,6 +44,7 @@ #include #include #include +#include #include "nodelist.h" @@ -98,6 +99,7 @@ static int jffs2_garbage_collect_thread(void *_c) { struct jffs2_sb_info *c = _c; + DECLARE_SWSUSP_LOCAL_VAR; daemonize(); current->tty = NULL; @@ -105,11 +107,16 @@ up(&c->gc_thread_start); sprintf(current->comm, "jffs2_gcd_mtd%d", c->mtd->index); + current->flags |= PF_SYNCTHREAD; + + SWSUSP_THREAD_FLAGS_RESET; + SWSUSP_ACTIVITY_START(PF_SYNCTHREAD); /* FIXME in the 2.2 backport */ current->nice = 10; for (;;) { + SWSUSP_ACTIVITY_SYNCTHREAD_PAUSING; spin_lock_irq(¤t->sigmask_lock); siginitsetinv (¤t->blocked, sigmask(SIGHUP) | sigmask(SIGKILL) | sigmask(SIGSTOP) | sigmask(SIGCONT)); recalc_sigpending(current); @@ -127,6 +134,7 @@ if (current->need_resched) schedule(); + SWSUSP_ACTIVITY_RESTARTING(PF_SYNCTHREAD); /* Put_super will send a SIGKILL and then wait on the sem. */ @@ -142,7 +150,9 @@ case SIGSTOP: D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): SIGSTOP received.\n")); set_current_state(TASK_STOPPED); + SWSUSP_ACTIVITY_SYNCTHREAD_PAUSING; schedule(); + SWSUSP_ACTIVITY_RESTARTING(PF_SYNCTHREAD); break; case SIGKILL: @@ -150,6 +160,7 @@ spin_lock_bh(&c->erase_completion_lock); c->gc_task = NULL; spin_unlock_bh(&c->erase_completion_lock); + SWSUSP_ACTIVITY_END; complete_and_exit(&c->gc_thread_exit, 0); case SIGHUP: diff -ruN linux-2.4.24/fs/jfs/jfs_logmgr.c software-suspend-linux-2.4.24-rev7/fs/jfs/jfs_logmgr.c --- linux-2.4.24/fs/jfs/jfs_logmgr.c 2004-01-22 19:47:35.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/fs/jfs/jfs_logmgr.c 2004-01-30 15:23:39.000000000 +1300 @@ -65,6 +65,7 @@ #include #include #include +#include #include "jfs_incore.h" #include "jfs_filsys.h" #include "jfs_metapage.h" @@ -2175,12 +2176,16 @@ int jfsIOWait(void *arg) { struct lbuf *bp; + DECLARE_SWSUSP_LOCAL_VAR; lock_kernel(); daemonize(); current->tty = NULL; strcpy(current->comm, "jfsIO"); + current->flags |= PF_SYNCTHREAD; + SWSUSP_THREAD_FLAGS_RESET; + SWSUSP_ACTIVITY_START(PF_SYNCTHREAD); unlock_kernel(); @@ -2205,13 +2210,16 @@ add_wait_queue(&jfs_IO_thread_wait, &wq); set_current_state(TASK_INTERRUPTIBLE); spin_unlock_irq(&log_redrive_lock); + SWSUSP_ACTIVITY_SYNCTHREAD_PAUSING; schedule(); + SWSUSP_ACTIVITY_RESTARTING(PF_SYNCTHREAD); current->state = TASK_RUNNING; remove_wait_queue(&jfs_IO_thread_wait, &wq); } while (!jfs_stop_threads); jfs_info("jfsIOWait being killed!"); complete(&jfsIOwait); + SWSUSP_ACTIVITY_END; return 0; } diff -ruN linux-2.4.24/fs/jfs/jfs_txnmgr.c software-suspend-linux-2.4.24-rev7/fs/jfs/jfs_txnmgr.c --- linux-2.4.24/fs/jfs/jfs_txnmgr.c 2004-01-22 19:46:09.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/fs/jfs/jfs_txnmgr.c 2004-01-30 15:23:39.000000000 +1300 @@ -47,6 +47,7 @@ #include #include #include +#include #include "jfs_incore.h" #include "jfs_filsys.h" #include "jfs_metapage.h" @@ -2758,12 +2759,17 @@ int WorkDone; struct tblock *tblk; unsigned long flags; + DECLARE_SWSUSP_LOCAL_VAR; lock_kernel(); daemonize(); current->tty = NULL; strcpy(current->comm, "jfsCommit"); + current->flags |= PF_SYNCTHREAD; + + SWSUSP_THREAD_FLAGS_RESET; + SWSUSP_ACTIVITY_START(PF_SYNCTHREAD); unlock_kernel(); @@ -2809,7 +2815,9 @@ * We can be running indefinately if other processors * are adding transactions to this list */ + SWSUSP_ACTIVITY_SYNCTHREAD_PAUSING; cond_resched(); + SWSUSP_ACTIVITY_RESTARTING(PF_SYNCTHREAD); LAZY_LOCK(flags); } @@ -2819,11 +2827,14 @@ add_wait_queue(&jfs_commit_thread_wait, &wq); set_current_state(TASK_INTERRUPTIBLE); LAZY_UNLOCK(flags); + SWSUSP_ACTIVITY_SYNCTHREAD_PAUSING; schedule(); + SWSUSP_ACTIVITY_RESTARTING(PF_SYNCTHREAD); current->state = TASK_RUNNING; remove_wait_queue(&jfs_commit_thread_wait, &wq); } while (!jfs_stop_threads); + SWSUSP_ACTIVITY_END; if (TxAnchor.unlock_queue) jfs_err("jfs_lazycommit being killed w/pending transactions!"); else @@ -2960,12 +2971,16 @@ struct jfs_inode_info *jfs_ip; int rc; tid_t tid; + DECLARE_SWSUSP_LOCAL_VAR; lock_kernel(); daemonize(); current->tty = NULL; strcpy(current->comm, "jfsSync"); + current->flags |= PF_SYNCTHREAD; + SWSUSP_THREAD_FLAGS_RESET; + SWSUSP_ACTIVITY_START(PF_SYNCTHREAD); unlock_kernel(); @@ -3006,7 +3021,9 @@ * Just to be safe. I don't know how * long we can run without blocking */ + SWSUSP_ACTIVITY_SYNCTHREAD_PAUSING; cond_resched(); + SWSUSP_ACTIVITY_RESTARTING(PF_SYNCTHREAD); TXN_LOCK(); } else { /* We can't get the commit semaphore. It may @@ -3031,11 +3048,14 @@ add_wait_queue(&jfs_sync_thread_wait, &wq); set_current_state(TASK_INTERRUPTIBLE); TXN_UNLOCK(); + SWSUSP_ACTIVITY_SYNCTHREAD_PAUSING; schedule(); + SWSUSP_ACTIVITY_RESTARTING(PF_SYNCTHREAD); current->state = TASK_RUNNING; remove_wait_queue(&jfs_sync_thread_wait, &wq); } while (!jfs_stop_threads); + SWSUSP_ACTIVITY_END; jfs_info("jfs_sync being killed"); complete(&jfsIOwait); return 0; diff -ruN linux-2.4.24/fs/lockd/clntlock.c software-suspend-linux-2.4.24-rev7/fs/lockd/clntlock.c --- linux-2.4.24/fs/lockd/clntlock.c 2004-01-22 19:50:29.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/fs/lockd/clntlock.c 2004-01-30 15:23:39.000000000 +1300 @@ -17,6 +17,7 @@ #include #include #include +#include #define NLMDBG_FACILITY NLMDBG_CLIENT @@ -200,6 +201,7 @@ struct list_head *tmp; struct file_lock *fl; struct inode *inode; + DECLARE_SWSUSP_LOCAL_VAR; daemonize(); reparent_to_init(); @@ -207,6 +209,11 @@ "%s-reclaim", host->h_name); + current->flags |= PF_SYNCTHREAD; + + SWSUSP_THREAD_FLAGS_RESET; + SWSUSP_ACTIVITY_START(PF_SYNCTHREAD); + /* This one ensures that our parent doesn't terminate while the * reclaim is in progress */ lock_kernel(); @@ -248,6 +255,7 @@ lockd_down(); unlock_kernel(); MOD_DEC_USE_COUNT; + SWSUSP_ACTIVITY_END; return 0; } diff -ruN linux-2.4.24/fs/lockd/clntproc.c software-suspend-linux-2.4.24-rev7/fs/lockd/clntproc.c --- linux-2.4.24/fs/lockd/clntproc.c 2004-01-22 19:45:40.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/fs/lockd/clntproc.c 2004-01-30 15:23:39.000000000 +1300 @@ -17,6 +17,7 @@ #include #include #include +#include #define NLMDBG_FACILITY NLMDBG_CLIENT #define NLMCLNT_GRACE_WAIT (5*HZ) @@ -243,6 +244,7 @@ struct file *filp = argp->lock.fl.fl_file; struct rpc_message msg; int status; + DECLARE_SWSUSP_LOCAL_VAR; dprintk("lockd: call procedure %s on %s\n", nlm_procname(proc), host->h_name); @@ -257,7 +259,9 @@ do { if (host->h_reclaiming && !argp->reclaim) { + SWSUSP_ACTIVITY_PAUSING; interruptible_sleep_on(&host->h_gracewait); + SWSUSP_ACTIVITY_RESTARTING(PF_SYNCTHREAD); continue; } @@ -298,7 +302,9 @@ } /* Back off a little and try again */ + SWSUSP_ACTIVITY_PAUSING; interruptible_sleep_on_timeout(&host->h_gracewait, 15*HZ); + SWSUSP_ACTIVITY_RESTARTING(PF_SYNCTHREAD); /* When the lock requested by F_SETLKW isn't available, we will wait until the request can be satisfied. If diff -ruN linux-2.4.24/fs/lockd/svc.c software-suspend-linux-2.4.24-rev7/fs/lockd/svc.c --- linux-2.4.24/fs/lockd/svc.c 2004-01-22 19:48:57.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/fs/lockd/svc.c 2004-01-30 15:23:39.000000000 +1300 @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -81,6 +82,7 @@ struct svc_serv *serv = rqstp->rq_server; int err = 0; unsigned long grace_period_expire; + DECLARE_SWSUSP_LOCAL_VAR; /* Lock module and set up kernel thread */ MOD_INC_USE_COUNT; @@ -95,6 +97,10 @@ daemonize(); reparent_to_init(); sprintf(current->comm, "lockd"); + current->flags |= PF_SYNCTHREAD; + + SWSUSP_THREAD_FLAGS_RESET; + SWSUSP_ACTIVITY_START(PF_SYNCTHREAD); /* Process request with signals blocked. */ spin_lock_irq(¤t->sigmask_lock); @@ -121,6 +127,11 @@ while ((nlmsvc_users || !signalled()) && nlmsvc_pid == current->pid) { long timeout = MAX_SCHEDULE_TIMEOUT; + + /* Chance for lockd to refrigerate */ + SWSUSP_ACTIVITY_SYNCTHREAD_PAUSING; + SWSUSP_ACTIVITY_RESTARTING(PF_SYNCTHREAD); + if (signalled()) { spin_lock_irq(¤t->sigmask_lock); flush_signals(current); @@ -198,6 +209,8 @@ /* release rpciod */ rpciod_down(); + SWSUSP_ACTIVITY_END; + /* Release module */ MOD_DEC_USE_COUNT; } diff -ruN linux-2.4.24/fs/locks.c software-suspend-linux-2.4.24-rev7/fs/locks.c --- linux-2.4.24/fs/locks.c 2004-01-22 19:50:30.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/fs/locks.c 2004-01-30 15:23:39.000000000 +1300 @@ -122,6 +122,7 @@ #include #include #include +#include #include #include @@ -606,13 +607,16 @@ { int result = 0; DECLARE_WAITQUEUE(wait, current); + DECLARE_SWSUSP_LOCAL_VAR; current->state = TASK_INTERRUPTIBLE; add_wait_queue(fl_wait, &wait); + SWSUSP_ACTIVITY_PAUSING; if (timeout == 0) schedule(); else result = schedule_timeout(timeout); + SWSUSP_ACTIVITY_RESTARTING(0); if (signal_pending(current)) result = -ERESTARTSYS; remove_wait_queue(fl_wait, &wait); @@ -1382,6 +1386,9 @@ { struct file *filp; int error, type; + DECLARE_SWSUSP_LOCAL_VAR; + + SWSUSP_ACTIVITY_START(0); error = -EBADF; filp = fget(fd); @@ -1409,6 +1416,7 @@ out_putf: fput(filp); out: + SWSUSP_ACTIVITY_END; return error; } diff -ruN linux-2.4.24/fs/namei.c software-suspend-linux-2.4.24-rev7/fs/namei.c --- linux-2.4.24/fs/namei.c 2004-01-22 19:46:33.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/fs/namei.c 2004-01-30 15:23:39.000000000 +1300 @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -1267,12 +1268,18 @@ char * tmp; struct dentry * dentry; struct nameidata nd; + DECLARE_SWSUSP_LOCAL_VAR; if (S_ISDIR(mode)) return -EPERM; + + SWSUSP_ACTIVITY_START(0); + tmp = getname(filename); - if (IS_ERR(tmp)) + if (IS_ERR(tmp)) { + SWSUSP_ACTIVITY_END; return PTR_ERR(tmp); + } error = path_lookup(tmp, LOOKUP_PARENT, &nd); if (error) @@ -1302,6 +1309,7 @@ out: putname(tmp); + SWSUSP_ACTIVITY_END; return error; } @@ -1335,7 +1343,9 @@ { int error = 0; char * tmp; + DECLARE_SWSUSP_LOCAL_VAR; + SWSUSP_ACTIVITY_START(0); tmp = getname(pathname); error = PTR_ERR(tmp); if (!IS_ERR(tmp)) { @@ -1358,6 +1368,7 @@ putname(tmp); } + SWSUSP_ACTIVITY_END; return error; } @@ -1433,10 +1444,14 @@ char * name; struct dentry *dentry; struct nameidata nd; + DECLARE_SWSUSP_LOCAL_VAR; + SWSUSP_ACTIVITY_START(0); name = getname(pathname); - if(IS_ERR(name)) + if(IS_ERR(name)) { + SWSUSP_ACTIVITY_END; return PTR_ERR(name); + } error = path_lookup(name, LOOKUP_PARENT, &nd); if (error) @@ -1465,6 +1480,7 @@ path_release(&nd); exit: putname(name); + SWSUSP_ACTIVITY_END; return error; } @@ -1501,10 +1517,14 @@ char * name; struct dentry *dentry; struct nameidata nd; + DECLARE_SWSUSP_LOCAL_VAR; + SWSUSP_ACTIVITY_START(0); name = getname(pathname); - if(IS_ERR(name)) + if(IS_ERR(name)) { + SWSUSP_ACTIVITY_END; return PTR_ERR(name); + } error = path_lookup(name, LOOKUP_PARENT, &nd); if (error) @@ -1529,6 +1549,7 @@ exit: putname(name); + SWSUSP_ACTIVITY_END; return error; slashes: @@ -1567,10 +1588,14 @@ int error = 0; char * from; char * to; + DECLARE_SWSUSP_LOCAL_VAR; + SWSUSP_ACTIVITY_START(0); from = getname(oldname); - if(IS_ERR(from)) + if(IS_ERR(from)) { + SWSUSP_ACTIVITY_END; return PTR_ERR(from); + } to = getname(newname); error = PTR_ERR(to); if (!IS_ERR(to)) { @@ -1592,6 +1617,7 @@ putname(to); } putname(from); + SWSUSP_ACTIVITY_END; return error; } @@ -1648,7 +1674,9 @@ { int error; char * to; + DECLARE_SWSUSP_LOCAL_VAR; + SWSUSP_ACTIVITY_START(0); to = getname(newname); error = PTR_ERR(to); if (!IS_ERR(to)) { @@ -1678,6 +1706,7 @@ exit: putname(to); } + SWSUSP_ACTIVITY_END; return error; } @@ -1921,10 +1950,14 @@ int error; char * from; char * to; + DECLARE_SWSUSP_LOCAL_VAR; + SWSUSP_ACTIVITY_START(0); from = getname(oldname); - if(IS_ERR(from)) + if(IS_ERR(from)) { + SWSUSP_ACTIVITY_END; return PTR_ERR(from); + } to = getname(newname); error = PTR_ERR(to); if (!IS_ERR(to)) { @@ -1932,6 +1965,7 @@ putname(to); } putname(from); + SWSUSP_ACTIVITY_END; return error; } diff -ruN linux-2.4.24/fs/namespace.c software-suspend-linux-2.4.24-rev7/fs/namespace.c --- linux-2.4.24/fs/namespace.c 2004-01-22 19:49:04.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/fs/namespace.c 2004-01-30 15:23:39.000000000 +1300 @@ -15,6 +15,7 @@ #include #include #include +#include #include @@ -365,7 +366,9 @@ { struct nameidata nd; int retval; + DECLARE_SWSUSP_LOCAL_VAR; + SWSUSP_ACTIVITY_START(0); retval = __user_walk(name, LOOKUP_POSITIVE|LOOKUP_FOLLOW, &nd); if (retval) goto out; @@ -383,6 +386,7 @@ dput_and_out: path_release(&nd); out: + SWSUSP_ACTIVITY_END; return retval; } @@ -831,10 +835,14 @@ unsigned long type_page; unsigned long dev_page; char *dir_page; + DECLARE_SWSUSP_LOCAL_VAR; + SWSUSP_ACTIVITY_START(0); retval = copy_mount_options (type, &type_page); - if (retval < 0) + if (retval < 0) { + SWSUSP_ACTIVITY_END; return retval; + } dir_page = getname(dir_name); retval = PTR_ERR(dir_page); @@ -861,6 +869,7 @@ putname(dir_page); out1: free_page(type_page); + SWSUSP_ACTIVITY_END; return retval; } @@ -905,10 +914,12 @@ struct vfsmount *tmp; struct nameidata new_nd, old_nd, parent_nd, root_parent, user_nd; int error; + DECLARE_SWSUSP_LOCAL_VAR; if (!capable(CAP_SYS_ADMIN)) return -EPERM; + SWSUSP_ACTIVITY_START(0); lock_kernel(); error = __user_walk(new_root, LOOKUP_POSITIVE|LOOKUP_FOLLOW|LOOKUP_DIRECTORY, &new_nd); @@ -978,6 +989,7 @@ path_release(&new_nd); out0: unlock_kernel(); + SWSUSP_ACTIVITY_END; return error; out3: spin_unlock(&dcache_lock); diff -ruN linux-2.4.24/fs/nfsd/nfssvc.c software-suspend-linux-2.4.24-rev7/fs/nfsd/nfssvc.c --- linux-2.4.24/fs/nfsd/nfssvc.c 2004-01-22 19:45:39.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/fs/nfsd/nfssvc.c 2004-01-30 15:23:39.000000000 +1300 @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -157,6 +158,7 @@ struct svc_serv *serv = rqstp->rq_server; int err; struct nfsd_list me; + DECLARE_SWSUSP_LOCAL_VAR; /* Lock module and set up kernel thread */ MOD_INC_USE_COUNT; @@ -164,6 +166,10 @@ daemonize(); sprintf(current->comm, "nfsd"); current->rlim[RLIMIT_FSIZE].rlim_cur = RLIM_INFINITY; + current->flags |= PF_SYNCTHREAD; + + SWSUSP_THREAD_FLAGS_RESET; + SWSUSP_ACTIVITY_START(PF_SYNCTHREAD); nfsdstats.th_cnt++; /* Let svc_process check client's authentication. */ @@ -251,6 +257,7 @@ /* Release module */ MOD_DEC_USE_COUNT; + SWSUSP_ACTIVITY_END; } static int diff -ruN linux-2.4.24/fs/open.c software-suspend-linux-2.4.24-rev7/fs/open.c --- linux-2.4.24/fs/open.c 2004-01-22 19:45:45.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/fs/open.c 2004-01-30 15:23:39.000000000 +1300 @@ -15,6 +15,7 @@ #include #include #include +#include #include @@ -41,7 +42,9 @@ { struct nameidata nd; int error; + DECLARE_SWSUSP_LOCAL_VAR; + SWSUSP_ACTIVITY_START(0); error = user_path_walk(path, &nd); if (!error) { struct statfs tmp; @@ -50,6 +53,7 @@ error = -EFAULT; path_release(&nd); } + SWSUSP_ACTIVITY_END; return error; } @@ -58,7 +62,9 @@ struct file * file; struct statfs tmp; int error; + DECLARE_SWSUSP_LOCAL_VAR; + SWSUSP_ACTIVITY_START(0); error = -EBADF; file = fget(fd); if (!file) @@ -68,6 +74,7 @@ error = -EFAULT; fput(file); out: + SWSUSP_ACTIVITY_END; return error; } @@ -120,7 +127,9 @@ struct nameidata nd; struct inode * inode; int error; + DECLARE_SWSUSP_LOCAL_VAR; + SWSUSP_ACTIVITY_START(0); error = -EINVAL; if (length < 0) /* sorry, but loff_t says... */ goto out; @@ -172,6 +181,7 @@ dput_and_out: path_release(&nd); out: + SWSUSP_ACTIVITY_END; return error; } @@ -187,7 +197,9 @@ struct dentry *dentry; struct file * file; int error; + DECLARE_SWSUSP_LOCAL_VAR; + SWSUSP_ACTIVITY_START(0); error = -EINVAL; if (length < 0) goto out; @@ -221,6 +233,7 @@ out_putf: fput(file); out: + SWSUSP_ACTIVITY_END; return error; } @@ -261,7 +274,9 @@ struct nameidata nd; struct inode * inode; struct iattr newattrs; + DECLARE_SWSUSP_LOCAL_VAR; + SWSUSP_ACTIVITY_START(0); error = user_path_walk(filename, &nd); if (error) goto out; @@ -290,6 +305,7 @@ dput_and_out: path_release(&nd); out: + SWSUSP_ACTIVITY_END; return error; } @@ -305,7 +321,9 @@ struct nameidata nd; struct inode * inode; struct iattr newattrs; + DECLARE_SWSUSP_LOCAL_VAR; + SWSUSP_ACTIVITY_START(0); error = user_path_walk(filename, &nd); if (error) @@ -335,6 +353,7 @@ dput_and_out: path_release(&nd); out: + SWSUSP_ACTIVITY_END; return error; } @@ -349,10 +368,12 @@ int old_fsuid, old_fsgid; kernel_cap_t old_cap; int res; + DECLARE_SWSUSP_LOCAL_VAR; if (mode & ~S_IRWXO) /* where's F_OK, X_OK, W_OK, R_OK? */ return -EINVAL; + SWSUSP_ACTIVITY_START(0); old_fsuid = current->fsuid; old_fsgid = current->fsgid; old_cap = current->cap_effective; @@ -380,6 +401,7 @@ current->fsgid = old_fsgid; current->cap_effective = old_cap; + SWSUSP_ACTIVITY_END; return res; } @@ -387,7 +409,9 @@ { int error; struct nameidata nd; + DECLARE_SWSUSP_LOCAL_VAR; + SWSUSP_ACTIVITY_START(0); error = __user_walk(filename,LOOKUP_POSITIVE|LOOKUP_FOLLOW|LOOKUP_DIRECTORY,&nd); if (error) goto out; @@ -401,6 +425,7 @@ dput_and_out: path_release(&nd); out: + SWSUSP_ACTIVITY_END; return error; } @@ -411,7 +436,9 @@ struct inode *inode; struct vfsmount *mnt; int error; + DECLARE_SWSUSP_LOCAL_VAR; + SWSUSP_ACTIVITY_START(0); error = -EBADF; file = fget(fd); if (!file) @@ -431,6 +458,7 @@ out_putf: fput(file); out: + SWSUSP_ACTIVITY_END; return error; } @@ -438,7 +466,9 @@ { int error; struct nameidata nd; + DECLARE_SWSUSP_LOCAL_VAR; + SWSUSP_ACTIVITY_START(0); error = __user_walk(filename, LOOKUP_POSITIVE | LOOKUP_FOLLOW | LOOKUP_DIRECTORY | LOOKUP_NOALT, &nd); if (error) @@ -458,6 +488,7 @@ dput_and_out: path_release(&nd); out: + SWSUSP_ACTIVITY_END; return error; } @@ -468,7 +499,9 @@ struct file * file; int err = -EBADF; struct iattr newattrs; + DECLARE_SWSUSP_LOCAL_VAR; + SWSUSP_ACTIVITY_START(0); file = fget(fd); if (!file) goto out; @@ -491,6 +524,7 @@ out_putf: fput(file); out: + SWSUSP_ACTIVITY_END; return err; } @@ -500,7 +534,9 @@ struct inode * inode; int error; struct iattr newattrs; + DECLARE_SWSUSP_LOCAL_VAR; + SWSUSP_ACTIVITY_START(0); error = user_path_walk(filename, &nd); if (error) goto out; @@ -523,6 +559,7 @@ dput_and_out: path_release(&nd); out: + SWSUSP_ACTIVITY_END; return error; } @@ -590,12 +627,15 @@ { struct nameidata nd; int error; + DECLARE_SWSUSP_LOCAL_VAR; + SWSUSP_ACTIVITY_START(0); error = user_path_walk(filename, &nd); if (!error) { error = chown_common(nd.dentry, user, group); path_release(&nd); } + SWSUSP_ACTIVITY_END; return error; } @@ -603,12 +643,15 @@ { struct nameidata nd; int error; + DECLARE_SWSUSP_LOCAL_VAR; + SWSUSP_ACTIVITY_START(0); error = user_path_walk_link(filename, &nd); if (!error) { error = chown_common(nd.dentry, user, group); path_release(&nd); } + SWSUSP_ACTIVITY_END; return error; } @@ -617,12 +660,15 @@ { struct file * file; int error = -EBADF; + DECLARE_SWSUSP_LOCAL_VAR; + SWSUSP_ACTIVITY_START(0); file = fget(fd); if (file) { error = chown_common(file->f_dentry, user, group); fput(file); } + SWSUSP_ACTIVITY_END; return error; } @@ -786,6 +832,9 @@ { char * tmp; int fd, error; + DECLARE_SWSUSP_LOCAL_VAR; + + SWSUSP_ACTIVITY_START(0); #if BITS_PER_LONG != 32 flags |= O_LARGEFILE; @@ -804,6 +853,7 @@ out: putname(tmp); } + SWSUSP_ACTIVITY_END; return fd; out_error: @@ -858,7 +908,11 @@ { struct file * filp; struct files_struct *files = current->files; + unsigned long has_fridge_wait = (current->flags & PF_FRIDGE_WAIT); + DECLARE_SWSUSP_LOCAL_VAR; + if (!has_fridge_wait) + SWSUSP_ACTIVITY_START(0); write_lock(&files->file_lock); if (fd >= files->max_fds) goto out_unlock; @@ -869,10 +923,14 @@ FD_CLR(fd, files->close_on_exec); __put_unused_fd(files, fd); write_unlock(&files->file_lock); + if (!has_fridge_wait) + SWSUSP_ACTIVITY_END; return filp_close(filp, files); out_unlock: write_unlock(&files->file_lock); + if (!has_fridge_wait) + SWSUSP_ACTIVITY_END; return -EBADF; } diff -ruN linux-2.4.24/fs/pipe.c software-suspend-linux-2.4.24-rev7/fs/pipe.c --- linux-2.4.24/fs/pipe.c 2004-01-22 19:45:37.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/fs/pipe.c 2004-01-30 15:23:39.000000000 +1300 @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -27,12 +28,16 @@ void pipe_wait(struct inode * inode) { DECLARE_WAITQUEUE(wait, current); + DECLARE_SWSUSP_LOCAL_VAR; + + SWSUSP_ACTIVITY_PAUSING; current->state = TASK_INTERRUPTIBLE; add_wait_queue(PIPE_WAIT(*inode), &wait); up(PIPE_SEM(*inode)); schedule(); remove_wait_queue(PIPE_WAIT(*inode), &wait); current->state = TASK_RUNNING; + SWSUSP_ACTIVITY_RESTARTING(0); down(PIPE_SEM(*inode)); } diff -ruN linux-2.4.24/fs/proc/generic.c software-suspend-linux-2.4.24-rev7/fs/proc/generic.c --- linux-2.4.24/fs/proc/generic.c 2004-01-22 19:50:59.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/fs/proc/generic.c 2004-01-30 15:23:39.000000000 +1300 @@ -14,6 +14,7 @@ #include #include #include +#include #define __NO_VERSION__ #include #include @@ -126,6 +127,8 @@ { struct inode *inode = file->f_dentry->d_inode; struct proc_dir_entry * dp; + ssize_t result; + DECLARE_SWSUSP_LOCAL_VAR; dp = (struct proc_dir_entry *) inode->u.generic_ip; @@ -133,7 +136,10 @@ return -EIO; /* FIXME: does this routine need ppos? probably... */ - return dp->write_proc(file, buffer, count, dp->data); + SWSUSP_ACTIVITY_PAUSING; /* Might be initiating suspend */ + result = dp->write_proc(file, buffer, count, dp->data); + SWSUSP_ACTIVITY_RESTARTING(0); + return result; } diff -ruN linux-2.4.24/fs/proc/kmsg.c software-suspend-linux-2.4.24-rev7/fs/proc/kmsg.c --- linux-2.4.24/fs/proc/kmsg.c 2004-01-22 19:47:38.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/fs/proc/kmsg.c 2004-01-30 15:23:39.000000000 +1300 @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -32,7 +33,13 @@ static ssize_t kmsg_read(struct file * file, char * buf, size_t count, loff_t *ppos) { - return do_syslog(2,buf,count); + ssize_t size; + DECLARE_SWSUSP_LOCAL_VAR; + + SWSUSP_ACTIVITY_PAUSING; + size = do_syslog(2,buf,count); + SWSUSP_ACTIVITY_RESTARTING(0); + return size; } static unsigned int kmsg_poll(struct file *file, poll_table * wait) diff -ruN linux-2.4.24/fs/read_write.c software-suspend-linux-2.4.24-rev7/fs/read_write.c --- linux-2.4.24/fs/read_write.c 2004-01-22 19:48:55.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/fs/read_write.c 2004-01-30 15:23:39.000000000 +1300 @@ -26,6 +26,7 @@ #include #include #include +#include #include @@ -109,6 +110,9 @@ { off_t retval; struct file * file; + DECLARE_SWSUSP_LOCAL_VAR; + + SWSUSP_ACTIVITY_START(0); retval = -EBADF; file = fget(fd); @@ -123,6 +127,7 @@ } fput(file); bad: + SWSUSP_ACTIVITY_END; return retval; } @@ -134,6 +139,9 @@ int retval; struct file * file; loff_t offset; + DECLARE_SWSUSP_LOCAL_VAR; + + SWSUSP_ACTIVITY_START(0); retval = -EBADF; file = fget(fd); @@ -155,6 +163,7 @@ out_putf: fput(file); bad: + SWSUSP_ACTIVITY_END; return retval; } #endif @@ -163,6 +172,9 @@ { ssize_t ret; struct file * file; + DECLARE_SWSUSP_LOCAL_VAR; + + SWSUSP_ACTIVITY_START(0); ret = -EBADF; file = fget(fd); @@ -181,6 +193,7 @@ dnotify_parent(file->f_dentry, DN_ACCESS); fput(file); } + SWSUSP_ACTIVITY_END; return ret; } @@ -188,6 +201,9 @@ { ssize_t ret; struct file * file; + DECLARE_SWSUSP_LOCAL_VAR; + + SWSUSP_ACTIVITY_START(0); ret = -EBADF; file = fget(fd); @@ -207,6 +223,7 @@ dnotify_parent(file->f_dentry, DN_MODIFY); fput(file); } + SWSUSP_ACTIVITY_END; return ret; } @@ -331,7 +348,9 @@ { struct file * file; ssize_t ret; + DECLARE_SWSUSP_LOCAL_VAR; + SWSUSP_ACTIVITY_START(0); ret = -EBADF; file = fget(fd); @@ -343,6 +362,7 @@ fput(file); bad_file: + SWSUSP_ACTIVITY_END; return ret; } @@ -351,7 +371,9 @@ { struct file * file; ssize_t ret; + DECLARE_SWSUSP_LOCAL_VAR; + SWSUSP_ACTIVITY_START(0); ret = -EBADF; file = fget(fd); @@ -363,6 +385,7 @@ fput(file); bad_file: + SWSUSP_ACTIVITY_END; return ret; } @@ -376,6 +399,9 @@ ssize_t ret; struct file * file; ssize_t (*read)(struct file *, char *, size_t, loff_t *); + DECLARE_SWSUSP_LOCAL_VAR; + + SWSUSP_ACTIVITY_START(0); ret = -EBADF; file = fget(fd); @@ -398,6 +424,7 @@ out: fput(file); bad_file: + SWSUSP_ACTIVITY_END; return ret; } @@ -407,6 +434,9 @@ ssize_t ret; struct file * file; ssize_t (*write)(struct file *, const char *, size_t, loff_t *); + DECLARE_SWSUSP_LOCAL_VAR; + + SWSUSP_ACTIVITY_START(0); ret = -EBADF; file = fget(fd); @@ -430,5 +460,6 @@ out: fput(file); bad_file: + SWSUSP_ACTIVITY_END; return ret; } diff -ruN linux-2.4.24/fs/reiserfs/journal.c software-suspend-linux-2.4.24-rev7/fs/reiserfs/journal.c --- linux-2.4.24/fs/reiserfs/journal.c 2004-01-22 19:48:45.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/fs/reiserfs/journal.c 2004-01-30 15:23:39.000000000 +1300 @@ -58,6 +58,7 @@ #include #include #include +#include /* the number of mounted filesystems. This is used to decide when to ** start and kill the commit thread @@ -1886,6 +1887,8 @@ */ static int reiserfs_journal_commit_thread(void *nullp) { + DECLARE_SWSUSP_LOCAL_VAR; + daemonize() ; spin_lock_irq(¤t->sigmask_lock); @@ -1894,7 +1897,11 @@ spin_unlock_irq(¤t->sigmask_lock); sprintf(current->comm, "kreiserfsd") ; + current->flags |= PF_SYNCTHREAD; lock_kernel() ; + + SWSUSP_THREAD_FLAGS_RESET; + SWSUSP_ACTIVITY_START(PF_SYNCTHREAD); while(1) { while(TQ_ACTIVE(reiserfs_commit_thread_tq)) { @@ -1907,10 +1914,13 @@ break ; } wake_up(&reiserfs_commit_thread_done) ; + SWSUSP_ACTIVITY_SYNCTHREAD_PAUSING; interruptible_sleep_on_timeout(&reiserfs_commit_thread_wait, 5 * HZ) ; + SWSUSP_ACTIVITY_RESTARTING(PF_SYNCTHREAD); } unlock_kernel() ; wake_up(&reiserfs_commit_thread_done) ; + SWSUSP_ACTIVITY_END; return 0 ; } diff -ruN linux-2.4.24/fs/stat.c software-suspend-linux-2.4.24-rev7/fs/stat.c --- linux-2.4.24/fs/stat.c 2004-01-22 19:46:00.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/fs/stat.c 2004-01-30 15:23:39.000000000 +1300 @@ -10,6 +10,7 @@ #include #include #include +#include #include @@ -138,6 +139,9 @@ { struct nameidata nd; int error; + DECLARE_SWSUSP_LOCAL_VAR; + + SWSUSP_ACTIVITY_START(0); error = user_path_walk(filename, &nd); if (!error) { @@ -146,6 +150,7 @@ error = cp_old_stat(nd.dentry->d_inode, statbuf); path_release(&nd); } + SWSUSP_ACTIVITY_END; return error; } #endif @@ -154,6 +159,9 @@ { struct nameidata nd; int error; + DECLARE_SWSUSP_LOCAL_VAR; + + SWSUSP_ACTIVITY_START(0); error = user_path_walk(filename, &nd); if (!error) { @@ -162,6 +170,7 @@ error = cp_new_stat(nd.dentry->d_inode, statbuf); path_release(&nd); } + SWSUSP_ACTIVITY_END; return error; } @@ -175,6 +184,9 @@ { struct nameidata nd; int error; + DECLARE_SWSUSP_LOCAL_VAR; + + SWSUSP_ACTIVITY_START(0); error = user_path_walk_link(filename, &nd); if (!error) { @@ -183,6 +195,7 @@ error = cp_old_stat(nd.dentry->d_inode, statbuf); path_release(&nd); } + SWSUSP_ACTIVITY_END; return error; } @@ -192,6 +205,9 @@ { struct nameidata nd; int error; + DECLARE_SWSUSP_LOCAL_VAR; + + SWSUSP_ACTIVITY_START(0); error = user_path_walk_link(filename, &nd); if (!error) { @@ -200,6 +216,7 @@ error = cp_new_stat(nd.dentry->d_inode, statbuf); path_release(&nd); } + SWSUSP_ACTIVITY_END; return error; } @@ -213,6 +230,9 @@ { struct file * f; int err = -EBADF; + DECLARE_SWSUSP_LOCAL_VAR; + + SWSUSP_ACTIVITY_START(0); f = fget(fd); if (f) { @@ -223,6 +243,7 @@ err = cp_old_stat(dentry->d_inode, statbuf); fput(f); } + SWSUSP_ACTIVITY_END; return err; } @@ -232,6 +253,9 @@ { struct file * f; int err = -EBADF; + DECLARE_SWSUSP_LOCAL_VAR; + + SWSUSP_ACTIVITY_START(0); f = fget(fd); if (f) { @@ -242,6 +266,7 @@ err = cp_new_stat(dentry->d_inode, statbuf); fput(f); } + SWSUSP_ACTIVITY_END; return err; } @@ -249,10 +274,13 @@ { struct nameidata nd; int error; + DECLARE_SWSUSP_LOCAL_VAR; if (bufsiz <= 0) return -EINVAL; + SWSUSP_ACTIVITY_START(0); + error = user_path_walk_link(path, &nd); if (!error) { struct inode * inode = nd.dentry->d_inode; @@ -265,6 +293,7 @@ } path_release(&nd); } + SWSUSP_ACTIVITY_END; return error; } @@ -335,6 +364,9 @@ { struct nameidata nd; int error; + DECLARE_SWSUSP_LOCAL_VAR; + + SWSUSP_ACTIVITY_START(0); error = user_path_walk(filename, &nd); if (!error) { @@ -343,6 +375,7 @@ error = cp_new_stat64(nd.dentry->d_inode, statbuf); path_release(&nd); } + SWSUSP_ACTIVITY_END; return error; } @@ -350,6 +383,9 @@ { struct nameidata nd; int error; + DECLARE_SWSUSP_LOCAL_VAR; + + SWSUSP_ACTIVITY_START(0); error = user_path_walk_link(filename, &nd); if (!error) { @@ -358,6 +394,7 @@ error = cp_new_stat64(nd.dentry->d_inode, statbuf); path_release(&nd); } + SWSUSP_ACTIVITY_END; return error; } @@ -365,6 +402,9 @@ { struct file * f; int err = -EBADF; + DECLARE_SWSUSP_LOCAL_VAR; + + SWSUSP_ACTIVITY_START(0); f = fget(fd); if (f) { @@ -375,6 +415,7 @@ err = cp_new_stat64(dentry->d_inode, statbuf); fput(f); } + SWSUSP_ACTIVITY_END; return err; } diff -ruN linux-2.4.24/fs/super.c software-suspend-linux-2.4.24-rev7/fs/super.c --- linux-2.4.24/fs/super.c 2004-01-22 19:47:22.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/fs/super.c 2004-01-30 15:23:39.000000000 +1300 @@ -28,6 +28,7 @@ #include #include #include +#include #include @@ -518,7 +519,9 @@ struct ustat tmp; struct statfs sbuf; int err = -EINVAL; + DECLARE_SWSUSP_LOCAL_VAR; + SWSUSP_ACTIVITY_START(0); s = get_super(to_kdev_t(dev)); if (s == NULL) goto out; @@ -533,6 +536,7 @@ err = copy_to_user(ubuf,&tmp,sizeof(struct ustat)) ? -EFAULT : 0; out: + SWSUSP_ACTIVITY_END; return err; } diff -ruN linux-2.4.24/include/asm-generic/bitops.h software-suspend-linux-2.4.24-rev7/include/asm-generic/bitops.h --- linux-2.4.24/include/asm-generic/bitops.h 2004-01-22 19:49:06.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/include/asm-generic/bitops.h 2004-01-30 15:23:39.000000000 +1300 @@ -51,6 +51,12 @@ return ((mask & *addr) != 0); } +/* + * fls: find last bit set. + */ + +#define fls(x) generic_fls(x) + #ifdef __KERNEL__ /* diff -ruN linux-2.4.24/include/asm-i386/bitops.h software-suspend-linux-2.4.24-rev7/include/asm-i386/bitops.h --- linux-2.4.24/include/asm-i386/bitops.h 2004-01-30 08:02:30.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/include/asm-i386/bitops.h 2004-01-30 15:23:39.000000000 +1300 @@ -330,6 +330,12 @@ return word; } +/* + * fls: find last bit set. + */ + +#define fls(x) generic_fls(x) + #ifdef __KERNEL__ /** diff -ruN linux-2.4.24/include/asm-i386/cpufeature.h software-suspend-linux-2.4.24-rev7/include/asm-i386/cpufeature.h --- linux-2.4.24/include/asm-i386/cpufeature.h 2004-01-22 19:49:28.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/include/asm-i386/cpufeature.h 2004-01-30 15:23:39.000000000 +1300 @@ -84,6 +84,7 @@ #define cpu_has_vme boot_cpu_has(X86_FEATURE_VME) #define cpu_has_de boot_cpu_has(X86_FEATURE_DE) #define cpu_has_pse boot_cpu_has(X86_FEATURE_PSE) +#define cpu_has_pse36 boot_cpu_has(X86_FEATURE_PSE36) #define cpu_has_tsc boot_cpu_has(X86_FEATURE_TSC) #define cpu_has_pae boot_cpu_has(X86_FEATURE_PAE) #define cpu_has_pge boot_cpu_has(X86_FEATURE_PGE) diff -ruN linux-2.4.24/include/asm-i386/suspend.h software-suspend-linux-2.4.24-rev7/include/asm-i386/suspend.h --- linux-2.4.24/include/asm-i386/suspend.h 1970-01-01 12:00:00.000000000 +1200 +++ software-suspend-linux-2.4.24-rev7/include/asm-i386/suspend.h 2004-01-30 15:23:39.000000000 +1300 @@ -0,0 +1,553 @@ + /* + * Copyright 2001-2002 Pavel Machek + * Based on code + * Copyright 2001 Patrick Mochel + */ +#include +#include +#include +#ifdef SUSPEND_C +#include +#endif +/* image of the saved processor states */ +struct saved_context { + u32 eax, ebx, ecx, edx; + u32 esp, ebp, esi, edi; + u16 es, fs, gs, ss; + u32 cr0, cr2, cr3, cr4; + u16 gdt_pad; + u16 gdt_limit; + u32 gdt_base; + u16 idt_pad; + u16 idt_limit; + u32 idt_base; + u16 ldt; + u16 tss; + u32 tr; + u32 safety; + u32 return_address; + u32 eflags; +} __attribute__((packed)); + +struct saved_context saved_contexts[NR_CPUS]; +struct saved_context saved_context; /* temporary storage */ + +spinlock_t saved_context_lock __nosavedata = SPIN_LOCK_UNLOCKED; + +#define loaddebug(thread,register) \ + __asm__("movl %0,%%db" #register \ + : /* no output */ \ + :"r" ((thread)->debugreg[register])) + + +/* + * save_processor_context + * + * Save the state of the processor before we go to sleep. + * + * return_stack is the value of the stack pointer (%esp) as the caller sees it. + * A good way could not be found to obtain it from here (don't want to make _too_ + * many assumptions about the layout of the stack this far down.) Also, the + * handy little __builtin_frame_pointer(level) where level > 0, is blatantly + * buggy - it returns the value of the stack at the proper location, not the + * location, like it should (as of gcc 2.91.66) + * + * Note that the context and timing of this function is pretty critical. + * With a minimal amount of things going on in the caller and in here, gcc + * does a good job of being just a dumb compiler. Watch the assembly output + * if anything changes, though, and make sure everything is going in the right + * place. + */ +static inline void save_processor_context(void) +{ + kernel_fpu_begin(); + + /* + * descriptor tables + */ + asm volatile ("sgdt (%0)" : "=m" (saved_context.gdt_limit)); + asm volatile ("sidt (%0)" : "=m" (saved_context.idt_limit)); + asm volatile ("sldt (%0)" : "=m" (saved_context.ldt)); + asm volatile ("str (%0)" : "=m" (saved_context.tr)); + + /* + * save the general registers. + * note that gcc has constructs to specify output of certain registers, + * but they're not used here, because it assumes that you want to modify + * those registers, so it tries to be smart and save them beforehand. + * It's really not necessary, and kinda fishy (check the assembly output), + * so it's avoided. + */ + asm volatile ("movl %%esp, (%0)" : "=m" (saved_context.esp)); + asm volatile ("movl %%eax, (%0)" : "=m" (saved_context.eax)); + asm volatile ("movl %%ebx, (%0)" : "=m" (saved_context.ebx)); + asm volatile ("movl %%ecx, (%0)" : "=m" (saved_context.ecx)); + asm volatile ("movl %%edx, (%0)" : "=m" (saved_context.edx)); + asm volatile ("movl %%ebp, (%0)" : "=m" (saved_context.ebp)); + asm volatile ("movl %%esi, (%0)" : "=m" (saved_context.esi)); + asm volatile ("movl %%edi, (%0)" : "=m" (saved_context.edi)); + + /* + * segment registers + */ + asm volatile ("movw %%es, %0" : "=r" (saved_context.es)); + asm volatile ("movw %%fs, %0" : "=r" (saved_context.fs)); + asm volatile ("movw %%gs, %0" : "=r" (saved_context.gs)); + asm volatile ("movw %%ss, %0" : "=r" (saved_context.ss)); + + /* + * control registers + */ + asm volatile ("movl %%cr0, %0" : "=r" (saved_context.cr0)); + asm volatile ("movl %%cr2, %0" : "=r" (saved_context.cr2)); + asm volatile ("movl %%cr3, %0" : "=r" (saved_context.cr3)); + asm volatile ("movl %%cr4, %0" : "=r" (saved_context.cr4)); + + /* + * eflags + */ + asm volatile ("pushfl ; popl (%0)" : "=m" (saved_context.eflags)); +} + +static void fix_processor_context(void) +{ + int nr = smp_processor_id(); + struct tss_struct * t = &init_tss[nr]; + + set_tss_desc(nr,t); /* This just modifies memory; should not be neccessary. But... This is neccessary, because 386 hardware has concept of busy tsc or some similar stupidity. */ + gdt_table[__TSS(nr)].b &= 0xfffffdff; + + load_TR(nr); /* This does ltr */ + + load_LDT(current->active_mm); /* This does lldt */ + + /* + * Now maybe reload the debug registers + */ + if (current->thread.debugreg[7]){ + loaddebug(¤t->thread, 0); + loaddebug(¤t->thread, 1); + loaddebug(¤t->thread, 2); + loaddebug(¤t->thread, 3); + /* no 4 and 5 */ + loaddebug(¤t->thread, 6); + loaddebug(¤t->thread, 7); + } + +} + +static void do_fpu_end(void) +{ + /* restore FPU regs if necessary */ + /* Do it out of line so that gcc does not move cr0 load to some stupid place */ + kernel_fpu_end(); +} + +/* + * restore_processor_context + * + * Restore the processor context as it was before we went to sleep + * - descriptor tables + * - control registers + * - segment registers + * - flags + * + * Note that it is critical that this function is declared inline. + * It was separated out from restore_state to make that function + * a little clearer, but it needs to be inlined because we won't have a + * stack when we get here (so we can't push a return address). + */ +static inline void restore_processor_context(void) +{ + /* + * first restore %ds, so we can access our data properly + */ + asm volatile (".align 4"); + asm volatile ("movw %0, %%ds" :: "r" ((u16)__KERNEL_DS)); + + + /* + * control registers + */ + asm volatile ("movl %0, %%cr4" :: "r" (saved_context.cr4)); + asm volatile ("movl %0, %%cr3" :: "r" (saved_context.cr3)); + asm volatile ("movl %0, %%cr2" :: "r" (saved_context.cr2)); + asm volatile ("movl %0, %%cr0" :: "r" (saved_context.cr0)); + + /* + * segment registers + */ + asm volatile ("movw %0, %%es" :: "r" (saved_context.es)); + asm volatile ("movw %0, %%fs" :: "r" (saved_context.fs)); + asm volatile ("movw %0, %%gs" :: "r" (saved_context.gs)); + asm volatile ("movw %0, %%ss" :: "r" (saved_context.ss)); + + /* + * the other general registers + * + * note that even though gcc has constructs to specify memory + * input into certain registers, it will try to be too smart + * and save them at the beginning of the function. This is esp. + * bad since we don't have a stack set up when we enter, and we + * want to preserve the values on exit. So, we set them manually. + */ + asm volatile ("movl %0, %%esp" :: "m" (saved_context.esp)); + asm volatile ("movl %0, %%ebp" :: "m" (saved_context.ebp)); + asm volatile ("movl %0, %%eax" :: "m" (saved_context.eax)); + asm volatile ("movl %0, %%ebx" :: "m" (saved_context.ebx)); + asm volatile ("movl %0, %%ecx" :: "m" (saved_context.ecx)); + asm volatile ("movl %0, %%edx" :: "m" (saved_context.edx)); + asm volatile ("movl %0, %%esi" :: "m" (saved_context.esi)); + asm volatile ("movl %0, %%edi" :: "m" (saved_context.edi)); + + /* + * now restore the descriptor tables to their proper values + * ltr is done i fix_processor_context(). + */ + + asm volatile ("lgdt (%0)" :: "m" (saved_context.gdt_limit)); + asm volatile ("lidt (%0)" :: "m" (saved_context.idt_limit)); + asm volatile ("lldt (%0)" :: "m" (saved_context.ldt)); + + fix_processor_context(); + + /* + * the flags + */ + asm volatile ("pushl %0 ; popfl" :: "m" (saved_context.eflags)); + + do_fpu_end(); +} + +#ifdef SUSPEND_C +/* Local variables for do_swsusp2_lowlevel */ +volatile static int loop __nosavedata = 0; +volatile static int state1 __nosavedata = 0; +volatile static int state2 __nosavedata = 0; +volatile static int state3 __nosavedata = 0; +volatile static struct range *origrange __nosavedata; +volatile static struct range *copyrange __nosavedata; +volatile static int origoffset __nosavedata; +volatile static int copyoffset __nosavedata; +volatile static unsigned long * origpage __nosavedata; +volatile static unsigned long * copypage __nosavedata; +#ifndef CONFIG_SMP +static unsigned long c_loops_per_jiffy_ref __nosavedata = 0; +static unsigned long cpu_khz_ref __nosavedata = 0; +#endif +extern atomic_t swsusp_cpu_counter __nosavedata; +extern inline void do_flush_tlb_all_local(void); + +/* + * APIC support: These routines save the APIC + * configuration for the CPU on which they are + * being executed + */ +extern void swsusp_apic_save_state(void); +extern void swsusp_apic_reload_state(void); + +#ifdef CONFIG_SMP +/* ------------------------------------------------ + * BEGIN Irq affinity code, based on code from LKCD. + * + * IRQ affinity support: + * Save and restore IRQ affinities, and set them + * all to CPU 0. + * + * Section between dashes taken from LKCD code. + * Perhaps we should be working toward a shared library + * of such routines for kexec, lkcd, software suspend + * and whatever other similar projects there are? + */ + +extern irq_desc_t irq_desc[]; +extern unsigned long irq_affinity[]; +unsigned long saved_affinity[NR_IRQS]; + +/* + * Routine to save the old irq affinities and change affinities of all irqs to + * the dumping cpu. + */ +static void set_irq_affinity(void) +{ + int i; + int cpu = smp_processor_id(); + + memcpy(saved_affinity, irq_affinity, NR_IRQS * sizeof(unsigned long)); + for (i = 0; i < NR_IRQS; i++) { + if (irq_desc[i].handler == NULL) + continue; + irq_affinity[i] = 1UL << cpu; + if (irq_desc[i].handler->set_affinity != NULL) + irq_desc[i].handler->set_affinity(i, irq_affinity[i]); + } +} + +/* + * Restore old irq affinities. + */ +static void reset_irq_affinity(void) +{ + int i; + + memcpy(irq_affinity, saved_affinity, NR_IRQS * sizeof(unsigned long)); + for (i = 0; i < NR_IRQS; i++) { + if (irq_desc[i].handler == NULL) + continue; + if (irq_desc[i].handler->set_affinity != NULL) + irq_desc[i].handler->set_affinity(i, saved_affinity[i]); + } +} + +/* + * END of IRQ affinity code, based on LKCD code. + * ----------------------------------------------------------------- + */ +#endif + +/* + * 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. + * + * SMP support: + * All SMP processors enter this routine during suspend. The one through + * which the suspend is initiated (which, for simplicity, is always CPU 0) + * sends the others here using an IPI during do_swsusp2_suspend_1. They + * remain here until after the atomic copy of the kernel is made, to ensure + * that they don't mess with memory in the meantime (even just idling will + * do that). Once the atomic copy is made, they are free to carry on idling. + * Note that we must let them go, because if we're using compression, the + * vfree calls in the compressors will result in IPIs being called and hanging + * because the CPUs are still here. + * + * At resume time, we do a similar thing. CPU 0 sends the others in here using + * an IPI. It then copies the original kernel back, restores its own processor + * context and flushes local tlbs before freeing the others to do the same. + * They can then go back to idling while CPU 0 reloads pageset 2, cleans up + * and unfreezes the processes. + * + * (Remember that freezing and thawing processes also uses IPIs, as may + * decompressing the data. Again, therefore, we cannot leave the other processors + * in here). + * + * At the moment, we do nothing about APICs, even though the code is there. + */ +void do_swsusp_lowlevel(int resume) +{ + if (!resume) { +#ifdef CONFIG_SMP + if (smp_processor_id() != cpu_logical_map(0)) { + unsigned long flags; + char * my_saved_context = (char *) &saved_contexts[smp_processor_id()]; + /* + *Save context and go back to idling. + * Note that we cannot leave the processor + * here. It must be able to receive IPIs if + * the LZF compression driver (eg) does a + * vfree after compressing the kernel etc + */ + mb(); + barrier(); + spin_lock_irqsave(&saved_context_lock, flags); + printnolog(SUSPEND_FREEZER, SUSPEND_MEDIUM, 0, + "Processor %d saving context...", smp_processor_id()); + PRINTPREEMPTCOUNT("Before save_processor_context."); + save_processor_context(); + for (loop = sizeof(struct saved_context); loop--; loop) + *(my_saved_context + loop - 1) = *(((char *) &saved_context) + loop - 1); + atomic_inc(&swsusp_cpu_counter); + printnolog(SUSPEND_FREEZER, SUSPEND_MEDIUM, 0, + "Processor %d context saved. CPU counter ->%d\n", + smp_processor_id(), atomic_read(&swsusp_cpu_counter)); + spin_unlock_irqrestore(&saved_context_lock, flags); + /* Now spin until the atomic copy of the kernel is made. */ + while (swsusp_state & FREEZE_SMP) { + cpu_relax(); + smp_mb(); + } + atomic_dec(&swsusp_cpu_counter); + return; + } + + /* + * Save the irq affinities before we freeze the + * other processors! + */ + set_irq_affinity(); +#endif + + do_swsusp2_suspend_1(); + PRINTPREEMPTCOUNT("Before save_processor_context."); + save_processor_context(); /* We need to capture registers and memory at "same time" */ + PRINTPREEMPTCOUNT("After save_processor_context."); + do_swsusp2_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_swsusp2_resume_1; copying works, because nr_copy_pages, + * pagedir_resume, loop and loop2 are nosavedata. + */ + +#ifdef CONFIG_PREEMPT + /* + * Preempt disabled in kernel we're about to restore. + * Make sure we match state now. + */ + preempt_disable(); + PRINTPREEMPTCOUNT("Prior to copying old kernel back."); +#endif + +#ifdef CONFIG_SMP + if (smp_processor_id() != cpu_logical_map(0)) { + unsigned long flags; + char * my_saved_context = (char *) &saved_contexts[smp_processor_id()]; + /* Save context and hold other processors here */ + atomic_inc(&swsusp_cpu_counter); + printnolog(SUSPEND_FREEZER, SUSPEND_MEDIUM, 0, + "Processor %d waiting for restoration of old kernel. CPU counter -> %d.\n", + smp_processor_id(), atomic_read(&swsusp_cpu_counter)); + smp_mb(); + while (swsusp_state & FREEZE_SMP) { + cpu_relax(); + smp_mb(); + } + spin_lock_irqsave(&saved_context_lock, flags); + for (loop = sizeof(struct saved_context); loop--; loop) + *(((char *) &saved_context) + loop - 1) = *(my_saved_context + loop - 1); + restore_processor_context(); + do_flush_tlb_all_local(); + atomic_dec(&swsusp_cpu_counter); + spin_unlock_irqrestore(&saved_context_lock, flags); + return; + } +#endif + + do_swsusp2_resume_1(); + + state1 = swsusp_action; + state2 = swsusp_debug_state; + state3 = console_loglevel; + +#ifdef CONFIG_SMP + /* Send all IRQs to CPU 0. We will replace the saved affinities + * with the suspend-time ones when we copy the original kernel + * back in place + */ + set_irq_affinity(); +#else + c_loops_per_jiffy_ref = cpu_data->loops_per_jiffy; + cpu_khz_ref = cpu_khz; +#endif + + origrange = pagedir_resume.origranges.first; + copyrange = pagedir_resume.destranges.first; + origoffset = origrange->minimum; + copyoffset = copyrange->minimum; + origpage = (unsigned long *) (page_address(mem_map + origoffset)); + copypage = (unsigned long *) (page_address(mem_map + copyoffset)); + + while (origrange) { + for (loop=0; loop < (PAGE_SIZE / sizeof(unsigned long)); loop++) + *(origpage + loop) = *(copypage + loop); + + if (origoffset < origrange->maximum) { + origoffset++; + origpage += (PAGE_SIZE / sizeof(unsigned long)); + } else { + origrange = origrange->next; + if (origrange) { + origoffset = origrange->minimum; + origpage = (unsigned long *) (page_address(mem_map + origoffset)); + } + } + + if (copyoffset < copyrange->maximum) { + copyoffset++; + copypage += (PAGE_SIZE / sizeof(unsigned long)); + } else { + copyrange = copyrange->next; + if (copyrange) { + copyoffset = copyrange->minimum; + copypage = (unsigned long *) (page_address(mem_map + copyoffset)); + } + } + } + + restore_processor_context(); +#ifdef CONFIG_SMP + do_flush_tlb_all_local(); +#else + __flush_tlb_all(); +#endif + + /* Get other CPUs to restore their contexts and flush their tlbs. */ + swsusp_state &= ~FREEZE_SMP; + + while (atomic_read(&swsusp_cpu_counter)) { + cpu_relax(); + smp_mb(); + } + +/* Ahah, we now run with our old stack, and with registers copied from + suspend time */ + +#ifdef CONFIG_SMP + /* put the irq affinity tables back */ + reset_irq_affinity(); +#else + cpu_data->loops_per_jiffy = c_loops_per_jiffy_ref; + loops_per_jiffy = c_loops_per_jiffy_ref; + cpu_khz = cpu_khz_ref; +#endif + swsusp_action = state1; + swsusp_debug_state = state2; + console_loglevel = state3; + + do_swsusp2_resume_2(); +} + +/* + * Function to put other smp processors in do_swsusp_lowlevel + * during suspend or resume. They get their CPU data saved and + * restored there + */ + +void smp_swsusp_lowlevel(void * info) +{ + unsigned long irq_lock_flags; + spinlock_t irq_lock = SPIN_LOCK_UNLOCKED; + + smp_mb(); + barrier(); + spin_lock_irqsave(&irq_lock, irq_lock_flags); + kernel_fpu_begin(); + do_swsusp_lowlevel(now_resuming); + barrier(); + smp_mb(); + kernel_fpu_end(); + spin_unlock_irqrestore(&irq_lock, irq_lock_flags); +} +#endif diff -ruN linux-2.4.24/include/linux/bitops.h software-suspend-linux-2.4.24-rev7/include/linux/bitops.h --- linux-2.4.24/include/linux/bitops.h 2004-01-30 08:02:30.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/include/linux/bitops.h 2004-01-30 15:23:39.000000000 +1300 @@ -1,6 +1,6 @@ #ifndef _LINUX_BITOPS_H #define _LINUX_BITOPS_H - +#include /* * ffs: find first bit set. This is defined the same way as @@ -38,6 +38,47 @@ } /* + * fls: find last bit set. + */ + +extern __inline__ int generic_fls(unsigned x) +{ + int r = 32; + + if (!x) + return 0; + if (!(x & 0xffff0000)) { + x <<= 16; + r -= 16; + } + if (!(x & 0xff000000)) { + x <<= 8; + r -= 8; + } + if (!(x & 0xf0000000)) { + x <<= 4; + r -= 4; + } + if (!(x & 0xc0000000)) { + x <<= 2; + r -= 2; + } + if (!(x & 0x80000000)) { + x <<= 1; + r -= 1; + } + return r; +} + +extern __inline__ int get_bitmask_order(unsigned int count) +{ + int order; + + order = fls(count); + return order; /* We could be slightly more clever with -1 here... */ +} + +/* * hweightN: returns the hamming weight (i.e. the number * of bits set) of a N-bit word */ @@ -66,7 +107,5 @@ return (res & 0x0F) + ((res >> 4) & 0x0F); } -#include - #endif diff -ruN linux-2.4.24/include/linux/init.h software-suspend-linux-2.4.24-rev7/include/linux/init.h --- linux-2.4.24/include/linux/init.h 2004-01-30 08:02:30.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/include/linux/init.h 2004-01-30 15:23:39.000000000 +1300 @@ -111,6 +111,9 @@ */ #define module_exit(x) __exitcall(x); +/* Data marked not to be saved by software_suspend() */ +#define __nosavedata __attribute__ ((__section__ (".data.nosave"))) + #else /* MODULE */ #define __init @@ -123,6 +126,9 @@ #define __FINIT #define __INITDATA +/* Data marked not to be saved by software_suspend() */ +#define __nosavedata __attribute__ ((__section__ (".data.nosave"))) + /* These macros create a dummy inline: gcc 2.9x does not count alias as usage, hence the `unused function' warning when __init functions are declared static. We use the dummy __*_module_inline functions diff -ruN linux-2.4.24/include/linux/mm.h software-suspend-linux-2.4.24-rev7/include/linux/mm.h --- linux-2.4.24/include/linux/mm.h 2004-01-30 08:17:02.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/include/linux/mm.h 2004-01-30 15:23:39.000000000 +1300 @@ -300,6 +300,7 @@ #define PG_reserved 14 #define PG_launder 15 /* written out by VM pressure.. */ #define PG_fs_1 16 /* Filesystem specific */ +#define PG_nosave 17 #ifndef arch_set_page_uptodate #define arch_set_page_uptodate(page) @@ -419,6 +420,10 @@ #define SetPageReserved(page) set_bit(PG_reserved, &(page)->flags) #define ClearPageReserved(page) clear_bit(PG_reserved, &(page)->flags) +#define PageNosave(page) test_bit(PG_nosave, &(page)->flags) +#define SetPageNosave(page) set_bit(PG_nosave, &(page)->flags) +#define ClearPageNosave(page) clear_bit(PG_nosave, &(page)->flags) + /* * Error return values for the *_nopage functions */ diff -ruN linux-2.4.24/include/linux/raid/md.h software-suspend-linux-2.4.24-rev7/include/linux/raid/md.h --- linux-2.4.24/include/linux/raid/md.h 2004-01-30 08:18:50.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/include/linux/raid/md.h 2004-01-30 15:23:39.000000000 +1300 @@ -86,6 +86,8 @@ extern void md_print_devices (void); +extern void md_autostart_arrays(void); + #define MD_BUG(x...) { printk("md: bug in file %s, line %d\n", __FILE__, __LINE__); md_print_devices(); } #endif diff -ruN linux-2.4.24/include/linux/reboot.h software-suspend-linux-2.4.24-rev7/include/linux/reboot.h --- linux-2.4.24/include/linux/reboot.h 2004-01-30 08:02:30.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/include/linux/reboot.h 2004-01-30 15:23:39.000000000 +1300 @@ -20,6 +20,7 @@ * CAD_OFF Ctrl-Alt-Del sequence sends SIGINT to init task. * POWER_OFF Stop OS and remove all power from system, if possible. * RESTART2 Restart system using given command string. + * SW_SUSPEND Suspend system using Software Suspend if compiled in */ #define LINUX_REBOOT_CMD_RESTART 0x01234567 @@ -28,6 +29,7 @@ #define LINUX_REBOOT_CMD_CAD_OFF 0x00000000 #define LINUX_REBOOT_CMD_POWER_OFF 0x4321FEDC #define LINUX_REBOOT_CMD_RESTART2 0xA1B2C3D4 +#define LINUX_REBOOT_CMD_SW_SUSPEND 0xD000FCE2 #ifdef __KERNEL__ @@ -46,6 +48,12 @@ extern void machine_halt(void); extern void machine_power_off(void); +/* + * Architecture-independent suspend facility + */ + +#include + #endif #endif /* _LINUX_REBOOT_H */ diff -ruN linux-2.4.24/include/linux/sched.h software-suspend-linux-2.4.24-rev7/include/linux/sched.h --- linux-2.4.24/include/linux/sched.h 2004-01-22 19:45:49.000000000 +1300 +++ software-suspend-linux-2.4.24-rev7/include/linux/sched.h 2004-01-30 15:23:39.000000000 +1300 @@ -431,8 +431,13 @@ #define PF_MEMALLOC 0x00000800 /* Allocating memory */ #define PF_FREE_PAGES 0x00002000 /* per process page freeing */ #define PF_NOIO 0x00004000 /* avoid generating further I/O */ - +#define PF_FROZEN 0x00010000 /* frozen for system suspend */ +#define PF_FREEZE 0x00020000 /* trying to freeze this task */ +#define PF_SYNCTHREAD 0x00040000 /* this thread can start activity during the + early part of freezing processes */ +#define PF_FRIDGE_WAIT 0x00080000 /* this thread is currently doing I/O */ #define PF_USEDFPU 0x00100000 /* task used FPU this quantum (SMP) */ +#define PF_NOFREEZE 0x00200000 /* this thread should never be frozen */ /* * Ptrace flags diff -ruN linux-2.4.24/include/linux/suspend-version-specific.h software-suspend-linux-2.4.24-rev7/include/linux/suspend-version-specific.h --- linux-2.4.24/include/linux/suspend-version-specific.h 1970-01-01 12:00:00.000000000 +1200 +++ software-suspend-linux-2.4.24-rev7/include/linux/suspend-version-specific.h 2004-01-30 15:23:39.000000000 +1300 @@ -0,0 +1,82 @@ +/* + * Software Suspend header file. + * + * This file sets the major and minor version numbers of the version specific + * part of the Software Suspend patch. The major and minor numbers must match + * those of the core patch. The extra is updated as necessary for version + * specific changes. + * + * It also contains the macros to be used for compiling the core code + * under this kernel version. + */ + +#ifndef SWSUSP_VERSION_SPECIFIC_REVISION +#define SWSUSP_VERSION_SPECIFIC_REVISION 0x201 +#define SWSUSP_VERSION_SPECIFIC_REVISION_STRING "2.0.0" + +#if defined(CONFIG_FBCON_SPLASHSCREEN) +#include +#include