Skip to contentSkip to conversation

suspend-to-{RAM,disk} for 2.5.17

PATCH Applied Pavel Machek 9 participants 30 messages May 21, 2002 – May 28, 2002

Subsystems (20)
lore
Share & tools
lore
#0 lore raw

Hi!

Here's suspend-to-{RAM,disk} combined patch for
2.5.17. Suspend-to-disk is pretty stable and was tested in 2.4-ac. Suspend-to-RAM is little more experimental, but works for me, and is certainly better than disk-eating version currently in kernel.

Major parts are: process stopper, S3 specific code, S4 specific code. What can I do to make this applied?

Pavel

Documentation/driver-model.txt +33 -20
@@ -52,7 +52,8 @@
 Each bus layer should implement the callbacks for these drivers. It then
 forwards the calls on to the device-specific callbacks. This means that
 device-specific drivers must still implement callbacks for each operation.
-But, they are not called from the top level driver layer.
+But, they are not called from the top level driver layer. [So for example
+PCI devices will not call device_register but pci_device_register.]
 
 This does add another layer of indirection for calling one of these functions,
 but there are benefits that are believed to outweigh this slowdown.
@@ -60,7 +61,7 @@
 First, it prevents device-specific drivers from having to know about the
 global device layer. This speeds up integration time incredibly. It also
 allows drivers to be more portable across kernel versions. Note that the
-former was intentional, the latter is an added bonus.
+former was intentional, the latter is an added bonus. 
 
 Second, this added indirection allows the bus to perform any additional logic
 necessary for its child devices. A bus layer may add additional information to
@@ -225,7 +226,6 @@
 	It also allows the platform driver (e.g. ACPI) to a driver without the driver
 	having to have explicit knowledge of (atrocities like) ACPI.
 
-
 current_state:
 	Current power state of the device. For PCI and other modern devices, this is
 	0-3, though it's not necessarily limited to those values.
@@ -251,18 +251,24 @@
 }
 
 probe:
-	Check for device existence and associate driver with it.
+	Check for device existence and associate driver with it. In case of device 
+	insertion, *all* drivers are called. Struct device has parent and bus_id 
+	valid at this point. probe() may only be called from process context. Returns
+	0 if it handles that device, -ESRCH if this driver does not know how to handle
+	this device, valid error otherwise.
 
 remove:
 	Dissociate driver with device. Releases device so that it could be used by
 	another driver. Also, if it is a hotplug device (hotplug PCI, Cardbus), an
-	ejection event could take place here.
+	ejection event could take place here. remove() can be called from interrupt 
+	context. [Fixme: Is that good?] Returns 0 on success. [Can we recover from
+	failed remove or should I define that remove() never fails?]
 
 suspend:
-	Perform one step of the device suspend process.
+	Perform one step of the device suspend process. Returns 0 on success.
 
 resume:
-	Perform one step of the device resume process.
+	Perform one step of the device resume process. Returns 0 on success.
 
 The probe() and remove() callbacks are intended to be much simpler than the
 current PCI correspondents.
@@ -275,7 +281,7 @@
 
 Some device initialisation was done in probe(). This should not be the case
 anymore. All initialisation should take place in the open() call for the
-device.
+device. [FIXME: How do you "open" uhci?]
 
 Breaking initialisation code out must also be done for the resume() callback,
 as most devices will have to be completely reinitialised when coming back from
@@ -324,6 +330,7 @@
 
 enum{
 	SUSPEND_NOTIFY,
+	SUSPEND_DISABLE,
 	SUSPEND_SAVE_STATE,
 	SUSPEND_POWER_DOWN,
 };
@@ -331,6 +338,7 @@
 enum {
 	RESUME_POWER_ON,
 	RESUME_RESTORE_STATE,
+	RESUME_ENABLE,
 };
 
 
@@ -352,9 +360,9 @@
 Instead, the walking of the device tree has been moved to userspace. When a
 user requests the system to suspend, it will walk the device tree, as exported
 via driverfs, and tell each device to go to sleep. It will do this multiple
-times based on what the system policy is.
-
-[ FIXME: URL pointer to the corresponding utility is missing here! ]
+times based on what the system policy is. [Not possible. Take ACPI enabled 
+system, with battery critically low. In such state, you want to suspend-to-disk,
+*fast*. User maybe is not even running powerd (think system startup)!]
 
 Device resume should happen in the same manner when the system awakens.
 
@@ -366,22 +374,25 @@
 cannot resume the hardware from the requested level, or it feels that it is
 too important to be put to sleep, it should return an error from this function.
 
-It does not have to stop I/O requests or actually save state at this point.
+It does not have to stop I/O requests or actually save state at this point. Called
+from process context.
 
 SUSPEND_DISABLE:
 
 The driver should stop taking I/O requests at this stage. Because the save
 state stage happens afterwards, the driver may not want to physically disable
-the device; only mark itself unavailable if possible.
+the device; only mark itself unavailable if possible. Called from process 
+context.
 
 SUSPEND_SAVE_STATE:
 
 The driver should allocate memory and save any device state that is relevant
-for the state it is going to enter.
+for the state it is going to enter. Called from process context.
 
 SUSPEND_POWER_DOWN:
 
-The driver should place the device in the power state requested.
+The driver should place the device in the power state requested. May be called
+from interrupt context.
 
 
 For resume, the stages are defined as follows:
@@ -389,25 +400,27 @@
 RESUME_POWER_ON:
 
 Devices should be powered on and reinitialised to some known working state.
+Called from process context.
 
 RESUME_RESTORE_STATE:
 
 The driver should restore device state to its pre-suspend state and free any
-memory allocated for its saved state.
+memory allocated for its saved state. Called from process context.
 
 RESUME_ENABLE:
 
-The device should start taking I/O requests again.
+The device should start taking I/O requests again. Called from process context.
 
 
 Each driver does not have to implement each stage. But, it if it does
-implemente a stage, it should do what is described above. It should not assume
+implement a stage, it should do what is described above. It should not assume
 that it performed any stage previously, or that it will perform any stage
-later.
+later. [Really? It makes sense to support SAVE_STATE only after DISABLE].
 
 It is quite possible that a driver can fail during the suspend process, for
 whatever reason. In this event, the calling process must gracefully recover
-and restore everything to their states before the suspend transition began.
+and restore everything to their states before the suspend transition began. 
+[Suspend may not fail, think battery low.]
 
 If a driver knows that it cannot suspend or resume properly, it should fail
 during the notify stage. Properly implemented power management schemes should
Documentation/swsusp.txt +159 -0

159 lines not on this page · raw message

MAINTAINERS +8 -0
@@ -1446,6 +1446,14 @@
 L:	[email protected]
 S:	Maintained
 
+SOFTWARE SUSPEND:
+P:	Gabor Kuti
+M:	[email protected]
+M:	[email protected]
+L:	http://lister.fornax.hu/mailman/listinfo/swsusp
+W:	http://falcon.sch.bme.hu/~seasons/linux
+S:	Maintained
+
 SONIC NETWORK DRIVER
 P:	Thomas Bogendoerfer
 M:	[email protected]
arch/i386/Config.help +25 -0
diff -ur -x .dep* -x .hdep* -x *.[oas] -x *~ -x #* -x *CVS* -x *.orig -x *.rej -x *.old -x .menu* -x asm -x local.h -x System.map -x autoconf.h -x compile.h -x version.h -x .version -x defkeymap.c -x uni_hash.tbl -x zImage -x vmlinux -x vmlinuz -x TAGS -x bootsect -x *RCS* -x conmakehash -x map -x build -x build -x configure -x *target* -x *.flags -x *.bak clean/arch/i386/Config.help linux-swsusp/arch/i386/Config.help
@@ -940,3 +940,28 @@
 CONFIG_DEBUG_OBSOLETE
   Say Y here if you want to reduce the chances of the tree compiling,
   and are prepared to dig into driver internals to fix compile errors.
+
+Software Suspend
+CONFIG_SOFTWARE_SUSPEND
+  Enable the possibilty of suspendig machine. It doesn't need APM.
+  You may suspend your machine by either pressing Sysrq-d or with
+  'swsusp' or 'shutdown -z <time>' (patch for sysvinit needed). It
+  creates an image which is saved in your active swaps. By the next
+  booting the kernel detects the saved image, restores the memory from
+  it and then it continues to run as before you've suspended.
+  If you don't want the previous state to continue use the 'noresume'
+  kernel option. However note that your partitions will be fsck'd and
+  you must re-mkswap your swap partitions/files.
+
+  Right now you may boot without resuming and then later resume but
+  in meantime you cannot use those swap partitions/files which were
+  involved in suspending. Also in this case there is a risk that buffers
+  on disk won't match with saved ones.
+
+  SMP is supported ``as-is''. There's a code for it but doesn't work.
+  There have been problems reported relating SCSI.
+  
+  This option is about getting stable. However there is still some
+  absence of features.
+
+  For more information take a look at Documentation/swsusp.txt.
arch/i386/boot/video.S +1 -0
@@ -438,6 +438,7 @@
 
 # Setting of user mode (AX=mode ID) => CF=success
 mode_set:
+	movw	%ax, %fs:(0x01fa)		# Store mode for use in acpi_wakeup.S
 	movw	%ax, %bx
 	cmpb	$0xff, %ah
 	jz	setalias
arch/i386/config.in +3 -0
@@ -396,6 +396,9 @@
 
 mainmenu_option next_comment
 comment 'Kernel hacking'
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+   dep_bool 'Software Suspend' CONFIG_SOFTWARE_SUSPEND $CONFIG_PM
+fi
 
 bool 'Kernel debugging' CONFIG_DEBUG_KERNEL
 if [ "$CONFIG_DEBUG_KERNEL" != "n" ]; then
arch/i386/defconfig +1 -0
@@ -119,6 +119,7 @@
 CONFIG_BINFMT_MISC=y
 CONFIG_PM=y
 # CONFIG_APM is not set
+# CONFIG_SOFTWARE_SUSPEND is not set
 
 #
 # Memory Technology Devices (MTD)
arch/i386/kernel/acpi.c +30 -0
@@ -44,6 +44,8 @@
 #include <asm/pgalloc.h>
 #include <asm/io_apic.h>
 #include <asm/tlbflush.h>
+#define ACPI_C
+#include <asm/suspend.h>
 
 
 #define PREFIX			"ACPI: "
@@ -621,6 +623,34 @@
 {
 	acpi_wakeup_address = (unsigned long)alloc_bootmem_low(PAGE_SIZE);
 	printk(KERN_DEBUG "ACPI: have wakeup address 0x%8.8lx\n", acpi_wakeup_address);
+}
+
+/*
+ * (KG): Since we affect stack here, we make this function as flat and easy
+ * as possible in order to not provoke gcc to use local variables on the stack.
+ * Note that on resume, all (expect nosave) variables will have the state from
+ * the time of writing (suspend_save_image) and the registers (including the
+ * stack pointer, but excluding the instruction pointer) will be loaded with 
+ * the values saved at save_processor_context() time.
+ */
+void do_suspend_magic(int resume)
+{
+	/* DANGER WILL ROBINSON!
+	 *
+	 * If this function is too difficult for gcc to optimize, it will crash and burn!
+	 * see above.
+	 *
+	 * DO NOT TOUCH.
+	 */
+	if (!resume) {
+		save_processor_context();
+		acpi_save_register_state((unsigned long)&&acpi_sleep_done);
+		acpi_enter_sleep_state(3);
+		return;
+	}
+acpi_sleep_done:
+	restore_processor_context();
+	printk("CPU context restored...\n");
 }
 
 #endif /*CONFIG_ACPI_SLEEP*/
arch/i386/kernel/acpi_wakeup.S +159 -19

259 lines not on this page · raw message

arch/i386/kernel/apm.c +1 -0
@@ -1667,6 +1667,7 @@
 	daemonize();
 
 	strcpy(current->comm, "kapmd");
+	current->flags |= PF_IOTHREAD;
 	sigfillset(&current->blocked);
 
 	if (apm_info.connection_version == 0) {
arch/i386/kernel/i8259.c +14 -1
@@ -11,6 +11,7 @@
 #include <linux/smp_lock.h>
 #include <linux/init.h>
 #include <linux/kernel_stat.h>
+#include <linux/device.h>
 
 #include <asm/atomic.h>
 #include <asm/system.h>
@@ -237,7 +238,19 @@
 	}
 }
 
-void __init init_8259A(int auto_eoi)
+static struct device device_i8259A = {
+	name:	       	"i8259A",
+	bus_id:		"0020",
+};
+
+static void __init init_8259A_devicefs(void)
+{
+	register_sys_device(&device_i8259A);
+}
+
+__initcall(init_8259A_devicefs);
+
+void init_8259A(int auto_eoi)
 {
 	unsigned long flags;
 
arch/i386/kernel/setup.c +5 -0
@@ -168,6 +168,8 @@
 static int disable_x86_serial_nr __initdata = 1;
 static int disable_x86_fxsr __initdata = 0;
 
+extern unsigned long saved_videomode;
+
 /*
  * This is set up by the setup-routine at boot-time
  */
@@ -182,6 +184,7 @@
 #define SYS_DESC_TABLE (*(struct sys_desc_table_struct*)(PARAM+0xa0))
 #define MOUNT_ROOT_RDONLY (*(unsigned short *) (PARAM+0x1F2))
 #define RAMDISK_FLAGS (*(unsigned short *) (PARAM+0x1F8))
+#define VIDEO_MODE (*(unsigned short *) (PARAM+0x1FA))
 #define ORIG_ROOT_DEV (*(unsigned short *) (PARAM+0x1FC))
 #define AUX_DEVICE_INFO (*(unsigned char *) (PARAM+0x1FF))
 #define LOADER_TYPE (*(unsigned char *) (PARAM+0x210))
@@ -681,6 +684,8 @@
  	drive_info = DRIVE_INFO;
  	screen_info = SCREEN_INFO;
 	apm_info.bios = APM_BIOS_INFO;
+	saved_videomode = VIDEO_MODE;
+	printk("Video mode to be used for restore is %lx\n", saved_videomode);
 	if( SYS_DESC_TABLE.length != 0 ) {
 		MCA_bus = SYS_DESC_TABLE.table[3] &0x2;
 		machine_id = SYS_DESC_TABLE.table[0];
arch/i386/kernel/signal.c +7 -0
@@ -21,6 +21,7 @@
 #include <linux/tty.h>
 #include <linux/personality.h>
 #include <linux/binfmts.h>
+#include <linux/suspend.h>
 #include <asm/ucontext.h>
 #include <asm/uaccess.h>
 #include <asm/i387.h>
@@ -594,6 +595,11 @@
 	if ((regs->xcs & 3) != 3)
 		return 1;
 
+	if (current->flags & PF_FREEZE) {
+		refrigerator(0);
+		goto no_signal;
+	}
+
 	if (!oldset)
 		oldset = &current->blocked;
 
@@ -701,6 +707,7 @@
 		return 1;
 	}
 
+ no_signal:
 	/* Did we come from a system call? */
 	if (regs->orig_eax >= 0) {
 		/* Restart the system call - no handlers present */
arch/i386/kernel/time.c +12 -0
diff -ur -x .dep* -x .hdep* -x *.[oas] -x *~ -x #* -x *CVS* -x *.orig -x *.rej -x *.old -x .menu* -x asm -x local.h -x System.map -x autoconf.h -x compile.h -x version.h -x .version -x defkeymap.c -x uni_hash.tbl -x zImage -x vmlinux -x vmlinuz -x TAGS -x bootsect -x *RCS* -x conmakehash -x map -x build -x build -x configure -x *target* -x *.flags -x *.bak clean/arch/i386/kernel/time.c linux-swsusp/arch/i386/kernel/time.c
@@ -42,6 +42,7 @@
 #include <linux/init.h>
 #include <linux/smp.h>
 #include <linux/module.h>
+#include <linux/device.h>
 
 #include <asm/io.h>
 #include <asm/smp.h>
@@ -635,6 +636,17 @@
 bad_ctc:
 	return 0;
 }
+
+static struct device device_i8253;
+
+static void time_init_driverfs(void)
+{
+	strcpy(device_i8253.name, "i8253");
+	strcpy(device_i8253.bus_id, "0040");
+	register_sys_device(&device_i8253);
+}
+
+__initcall(time_init_driverfs);
 
 void __init time_init(void)
 {
arch/i386/vmlinux.lds +6 -0
@@ -66,6 +66,12 @@
   __init_end = .;
 
   . = ALIGN(4096);
+  __nosave_begin = .;
+  .data_nosave : { *(.data.nosave) }
+  . = ALIGN(4096);
+  __nosave_end = .;
+
+  . = ALIGN(4096);
   .data.page_aligned : { *(.data.idt) }
 
   . = ALIGN(32);
drivers/acpi/acpi_system.c +31 -7
@@ -33,6 +33,7 @@
 #include <linux/delay.h>
 #include <linux/sysrq.h>
 #include <linux/pm.h>
+#include <linux/device.h>
 #include <asm/uaccess.h>
 #include <asm/acpi.h>
 #include "acpi_bus.h"
@@ -128,6 +129,12 @@
 	/* restore device context */
 	device_resume(RESUME_RESTORE_STATE);
 #endif
+
+	if (dmi_broken & BROKEN_INIT_AFTER_S1) {
+		printk("Broken toshiba laptop -> kicking interrupts\n");
+		init_8259A(0);
+	}
+
 	return AE_OK;
 }
 
@@ -254,23 +261,21 @@
 	switch (state)
 	{
 	case ACPI_STATE_S1:
-		/* do nothing */
+		barrier();
+		status = acpi_enter_sleep_state(state);
 		break;
 
 	case ACPI_STATE_S2:
 	case ACPI_STATE_S3:
-		acpi_save_register_state((unsigned long)&&acpi_sleep_done);
+		do_suspend_magic(0);
 		break;
 	}
 
-	barrier();
-	status = acpi_enter_sleep_state(state);
-
-acpi_sleep_done:
-
+	printk("acpi_restore_register_state...");
 	acpi_restore_register_state();
 	restore_flags(flags);
 
+	printk("acpi returning...");
 	return status;
 }
 
@@ -290,6 +295,8 @@
 	if (state < ACPI_STATE_S1 || state > ACPI_STATE_S5)
 		return AE_ERROR;
 
+	freeze_processes();
+
 	/* do we have a wakeup address for S2 and S3? */
 	if (state == ACPI_STATE_S2 || state == ACPI_STATE_S3) {
 		if (!acpi_wakeup_address)
@@ -315,7 +322,9 @@
 	 * no matter what.
 	 */
 	acpi_system_restore_state(state);
+	printk("acpi_leave_sleep_state...");
 	acpi_leave_sleep_state(state);
+	printk("ook\n");
 
 	/* make sure interrupts are enabled */
 	ACPI_ENABLE_IRQS();
@@ -323,6 +332,8 @@
 	/* reset firmware waking vector */
 	acpi_set_firmware_waking_vector((ACPI_PHYSICAL_ADDRESS) 0);
 
+	thaw_processes();
+
 	return status;
 }
 
@@ -700,8 +773,17 @@
 	
 	if (!system->states[state])
 		return_VALUE(-ENODEV);
+
 	
+#ifdef CONFIG_SOFTWARE_SUSPEND
+	if (state == 4) {
+		/* We are working from process context, that's why we may call it directly. */ 
+		do_software_suspend();
+		return_VALUE(count);
+	}
+#endif
 	status = acpi_suspend(state);
+
 	if (ACPI_FAILURE(status))
 		return_VALUE(-ENODEV);
 	
@@ -1180,6 +1262,10 @@
 		}
 	}
 	printk(")\n");
+#ifdef CONFIG_SOFTWARE_SUSPEND
+	printk(KERN_INFO "Software suspend => we can do S4.");
+	system->states[4] = 1;
+#endif
 
 #ifdef CONFIG_PM
 	/* Install the soft-off (S5) handler. */
drivers/block/floppy.c +5 -0
@@ -173,6 +173,7 @@
 #include <linux/interrupt.h>
 #include <linux/init.h>
 #include <linux/devfs_fs_kernel.h>
+#include <linux/device.h>
 
 /*
  * PS/2 floppies have much slower step rates than regular floppies.
@@ -4178,11 +4179,15 @@
 
 static int have_no_fdc= -ENODEV;
 
+static struct device device_floppy;
 
 int __init floppy_init(void)
 {
 	int i,unit,drive;
 
+	strcpy(device_floppy.name, "floppy");
+	strcpy(device_floppy.bus_id, "03?0");
+	register_sys_device(&device_floppy);
 
 	raw_cmd = NULL;
 
drivers/block/ll_rw_blk.c +3 -1
@@ -1418,7 +1419,9 @@
 	req->buffer = bio_data(bio);	/* see ->buffer comment above */
 	req->waiting = NULL;
 	req->bio = req->biotail = bio;
-	req->rq_dev = to_kdev_t(bio->bi_bdev->bd_dev);
+	if (bio->bi_bdev)
+		req->rq_dev = to_kdev_t(bio->bi_bdev->bd_dev);
+	else	req->rq_dev = NODEV;
 	add_request(q, req, insert_here);
 out:
 	if (freereq)
drivers/block/loop.c +3 -2
@@ -71,11 +71,11 @@
 #include <linux/smp_lock.h>
 #include <linux/swap.h>
 #include <linux/slab.h>
+#include <linux/loop.h>
+#include <linux/suspend.h>
 
 #include <asm/uaccess.h>
 
-#include <linux/loop.h>		
-
 #define MAJOR_NR LOOP_MAJOR
 
 static int max_loop = 8;
@@ -534,6 +534,7 @@
 	daemonize();
 
 	sprintf(current->comm, "loop%d", lo->lo_number);
+	current->flags |= PF_IOTHREAD;
 
 	spin_lock_irq(&current->sigmask_lock);
 	sigfillset(&current->blocked);
drivers/char/sysrq.c +21 -0
@@ -27,6 +27,7 @@
 #include <linux/quotaops.h>
 #include <linux/smp_lock.h>
 #include <linux/module.h>
+#include <linux/suspend.h>
 
 #include <linux/spinlock.h>
 
@@ -317,6 +318,22 @@
 	action_msg:	"Kill All Tasks",
 };
 
+#ifdef CONFIG_SOFTWARE_SUSPEND
+static void sysrq_handle_swsusp(int key, struct pt_regs *pt_regs,
+		struct kbd_struct *kbd, struct tty_struct *tty) {
+        if(!software_suspend_enabled) {
+		printk("Software Suspend is not possible now\n");
+		return;
+	}
+	software_suspend();
+}
+static struct sysrq_key_op sysrq_swsusp_op = {
+	handler:	sysrq_handle_swsusp,
+	help_msg:	"suspenD",
+	action_msg:	"Software suspend\n",
+};
+#endif
+
 /* END SIGNAL SYSRQ HANDLERS BLOCK */
 
 
@@ -339,7 +356,11 @@
 		 and will never arive */
 /* b */	&sysrq_reboot_op,
 /* c */	NULL,
+#ifdef CONFIG_SOFTWARE_SUSPEND
+/* d */	&sysrq_swsusp_op,
+#else
 /* d */	NULL,
+#endif
 /* e */	&sysrq_term_op,
 /* f */	NULL,
 /* g */	NULL,
drivers/ide/ide-disk.c +3 -0
@@ -27,6 +27,7 @@
 #include <linux/slab.h>
 #include <linux/delay.h>
 #include <linux/ide.h>
+#include <linux/suspend.h>
 
 #include <asm/byteorder.h>
 #include <asm/irq.h>
@@ -561,6 +562,8 @@
 	/* I hope that every freeze operations from the upper levels have
 	 * already been done...
 	 */
+
+	BUG_ON(in_interrupt());
 
 	if (level != SUSPEND_SAVE_STATE)
 		return 0;
drivers/pci/power.c +2 -2
@@ -110,7 +110,7 @@
 	return error;
 }
 
-static int pci_pm_suspend(u32 state)
+int pci_pm_suspend(u32 state)
 {
 	struct list_head *list;
 	struct pci_bus *bus;
@@ -123,7 +123,7 @@
 	return 0;
 }
 
-static int pci_pm_resume(void)
+int pci_pm_resume(void)
 {
 	struct list_head *list;
 	struct pci_bus *bus;
drivers/usb/storage/usb.c +1 -0
@@ -323,6 +323,7 @@
 	/* avoid getting signals */
 	spin_lock_irq(&current->sigmask_lock);
 	flush_signals(current);
+	current->flags |= PF_IOTHREAD;
 	sigfillset(&current->blocked);
 	recalc_sigpending();
 	spin_unlock_irq(&current->sigmask_lock);
fs/buffer.c +3 -0
@@ -32,6 +32,7 @@
 #include <linux/writeback.h>
 #include <linux/mempool.h>
 #include <linux/hash.h>
+#include <linux/suspend.h>
 #include <asm/bitops.h>
 
 #define BH_ENTRY(list) list_entry((list), struct buffer_head, b_assoc_buffers)
@@ -122,6 +123,8 @@
 	smp_mb__after_clear_bit();
 	wake_up_buffer(bh);
 }
+
+DECLARE_TASK_QUEUE(tq_bdflush);
 
 /*
  * Block until a buffer comes unlocked.  This doesn't stop it
fs/jbd/journal.c +11 -1
@@ -32,6 +32,7 @@
 #include <linux/init.h>
 #include <linux/mm.h>
 #include <linux/slab.h>
+#include <linux/suspend.h>
 #include <linux/pagemap.h>
 #include <asm/uaccess.h>
 #include <linux/proc_fs.h>
@@ -225,6 +226,7 @@
 			journal->j_commit_interval / HZ);
 	list_add(&journal->j_all_journals, &all_journals);
 
+	current->flags |= PF_KERNTHREAD;
 	/* And now, wait forever for commit wakeup events. */
 	while (1) {
 		if (journal->j_flags & JFS_UNMOUNT)
@@ -245,7 +247,15 @@
 		}
 
 		wake_up(&journal->j_wait_done_commit);
-		interruptible_sleep_on(&journal->j_wait_commit);
+		if (current->flags & PF_FREEZE) { /* The simpler the better. Flushing journal isn't a
+						     good idea, because that depends on threads that
+						     may be already stopped. */
+			jbd_debug(1, "Now suspending kjournald\n");
+			refrigerator(PF_IOTHREAD);
+			jbd_debug(1, "Resuming kjournald\n");						
+		} else		/* we assume on resume that commits are already there,
+				   so we don't sleep */
+			interruptible_sleep_on(&journal->j_wait_commit);
 
 		jbd_debug(1, "kjournald wakes\n");
 
fs/reiserfs/journal.c +8 -1
@@ -57,6 +57,7 @@
 #include <linux/stat.h>
 #include <linux/string.h>
 #include <linux/smp_lock.h>
+#include <linux/suspend.h> 
 
 /* the number of mounted filesystems.  This is used to decide when to
 ** start and kill the commit thread
@@ -1886,6 +1887,7 @@
   spin_unlock_irq(&current->sigmask_lock);
 
   sprintf(current->comm, "kreiserfsd") ;
+  current->flags |= PF_KERNTHREAD;
   lock_kernel() ;
   while(1) {
 
@@ -1899,7 +1901,12 @@
       break ;
     }
     wake_up(&reiserfs_commit_thread_done) ;
-    interruptible_sleep_on_timeout(&reiserfs_commit_thread_wait, 5 * HZ) ;
+#ifdef CONFIG_SOFTWARE_SUSPEND
+    if (current->flags & PF_FREEZE) {
+	    refrigerator(PF_IOTHREAD);
+    } else
+#endif
+	    interruptible_sleep_on_timeout(&reiserfs_commit_thread_wait, 5 * HZ) ;
   }
   unlock_kernel() ;
   wake_up(&reiserfs_commit_thread_done) ;
include/asm-generic/bitops.h +6 -0
@@ -51,6 +51,12 @@
 	return ((mask & *addr) != 0);
 }
 
+/*
+ * fls: find last bit set.
+ */
+
+#define fls(x) generic_fls(x)
+
 #ifdef __KERNEL__
 
 /*
include/asm-i386/bitops.h +6 -0
@@ -414,6 +414,12 @@
 	return word;
 }
 
+/*
+ * fls: find last bit set.
+ */
+
+#define fls(x) generic_fls(x)
+
 #ifdef __KERNEL__
 
 /*
include/asm-i386/suspend.h +304 -0

304 lines not on this page · raw message

include/linux/bitops.h +42 -1

54 lines not on this page · raw message

include/linux/init.h +3 -0
@@ -169,6 +169,9 @@
 
 #endif
 
+/* Data marked not to be saved by software_suspend() */
+#define __nosavedata __attribute__ ((__section__ (".data.nosave")))
+
 #ifdef CONFIG_HOTPLUG
 #define __devinit
 #define __devinitdata
include/linux/page-flags.h +7 -0
@@ -64,6 +64,7 @@
 
 #define PG_private		12	/* Has something at ->private */
 #define PG_writeback		13	/* Page is under writeback */
+#define PG_nosave		15	/* Used for system suspend/resume */
 
 /*
  * Global page accounting.  One instance per CPU.
@@ -206,6 +207,12 @@
 			dec_page_state(nr_writeback);			\
 		ret;							\
 	})
+
+#define PageNosave(page)	test_bit(PG_nosave, &(page)->flags)
+#define SetPageNosave(page)	set_bit(PG_nosave, &(page)->flags)
+#define TestSetPageNosave(page)	test_and_set_bit(PG_nosave, &(page)->flags)
+#define ClearPageNosave(page)		clear_bit(PG_nosave, &(page)->flags)
+#define TestClearPageNosave(page)	test_and_clear_bit(PG_nosave, &(page)->flags)
 
 /*
  * The PageSwapCache predicate doesn't use a PG_flag at this time,
include/linux/reboot.h +9 -0
@@ -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__
@@ -45,6 +47,13 @@
 extern void machine_restart(char *cmd);
 extern void machine_halt(void);
 extern void machine_power_off(void);
+
+/*
+ * Architecture-independent suspend facility
+ */
+
+extern void software_suspend(void);
+extern unsigned char software_suspend_enabled;
 
 #endif
 
include/linux/sched.h +5 -0
@@ -388,6 +388,11 @@
 #define PF_FLUSHER	0x00004000	/* responsible for disk writeback */
 #define PF_RADIX_TREE	0x00008000	/* debug: performing radix tree alloc */
 
+#define PF_FREEZE	0x00010000	/* this task should be frozen for suspend */
+#define PF_IOTHREAD	0x00020000	/* this thread is needed for doing I/O to swap */
+#define PF_KERNTHREAD	0x00040000	/* this thread is a kernel thread that cannot be sent signals to */
+#define PF_FROZEN	0x00080000	/* frozen for system suspend */
+
 /*
  * Ptrace flags
  */
include/linux/suspend.h +65 -0

65 lines not on this page · raw message

include/linux/tqueue.h +1 -1
@@ -66,7 +66,7 @@
 #define DECLARE_TASK_QUEUE(q)	LIST_HEAD(q)
 #define TQ_ACTIVE(q)		(!list_empty(&q))
 
-extern task_queue tq_timer, tq_immediate, tq_disk;
+extern task_queue tq_timer, tq_immediate, tq_disk, tq_bdflush;
 
 /*
  * To implement your own list of active bottom halfs, use the following
init/do_mounts.c +6 -0
@@ -10,6 +10,7 @@
 #include <linux/fd.h>
 #include <linux/tty.h>
 #include <linux/init.h>
+#include <linux/suspend.h>
 
 #include <linux/nfs_fs.h>
 #include <linux/nfs_fs_sb.h>
@@ -825,6 +826,11 @@
 #endif
 
 	create_dev("/dev/root", ROOT_DEV, NULL);
+
+	/* This has to be before mounting root, because even readonly mount of reiserfs would replay
+	   log corrupting stuff */
+	software_resume();
+
 	if (mount_initrd) {
 		if (initrd_load() && !kdev_same(ROOT_DEV, mk_kdev(RAMDISK_MAJOR, 0))) {
 			handle_initrd();
kernel/Makefile +2 -1
@@ -10,7 +10,7 @@
 O_TARGET := kernel.o
 
 export-objs = signal.o sys.o kmod.o context.o ksyms.o pm.o exec_domain.o \
-		printk.o platform.o 
+		printk.o platform.o suspend.o
 
 obj-y     = sched.o dma.o fork.o exec_domain.o panic.o printk.o \
 	    module.o exit.o itimer.o info.o time.o softirq.o resource.o \
@@ -21,6 +21,7 @@
 obj-$(CONFIG_MODULES) += ksyms.o
 obj-$(CONFIG_PM) += pm.o
 obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o
+obj-$(CONFIG_SOFTWARE_SUSPEND) += suspend.o
 
 ifneq ($(CONFIG_IA64),y)
 # According to Alan Modra <[email protected]>, the -fno-omit-frame-pointer is
kernel/context.c +1 -0
@@ -72,6 +72,7 @@
 
 	daemonize();
 	strcpy(curtask->comm, "keventd");
+	current->flags |= PF_IOTHREAD;
 	keventd_running = 1;
 	keventd_task = curtask;
 
kernel/signal.c +1 -1
@@ -493,7 +493,7 @@
  * No need to set need_resched since signal event passing
  * goes through ->blocked
  */
-static inline void signal_wake_up(struct task_struct *t)
+inline void signal_wake_up(struct task_struct *t)
 {
 	set_tsk_thread_flag(t,TIF_SIGPENDING);
 
kernel/softirq.c +1 -0
@@ -365,6 +365,7 @@
 
 	daemonize();
 	set_user_nice(current, 19);
+	current->flags |= PF_IOTHREAD;
 	sigfillset(&current->blocked);
 
 	/* Migrate to the right CPU */
kernel/suspend.c +1327 -0

1,327 lines not on this page · raw message

kernel/sys.c +11 -0
@@ -4,6 +4,7 @@
  *  Copyright (C) 1991, 1992  Linus Torvalds
  */
 
+#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/mm.h>
[… more lines in the raw message]

Lines of this file are not shown. View the raw message.

mm/page_alloc.c +41 -0

41 lines not on this page · raw message

mm/page_io.c +4 -0

Lines of this file are not shown. View the raw message.

mm/pdflush.c +10 -2

Lines of this file are not shown. View the raw message.

mm/vmscan.c +7 -2

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

Signature · 2 lines
(about SSSCA) "I don't say this lightly.  However, I really think that the U.S.
no longer is classifiable as a democracy, but rather as a plutocracy." --hpa

Part of the diff is not shown here. View the raw message.

#1 lore raw
Quoted 3 lines · Pavel Machek

Major parts are: process stopper, S3 specific code, S4 specific code. What can I do to make this applied?

Applied. Nothing needed but some time for me to look through it.

It still has a few too many #ifdef CONFIG_SUSPEND, and I get this feeling that the background deamons shouldn't need to do the "freeze()" by hand but simply be automatically frozen and thawed when they sleep by looking at the KERNTHREAD bit or something, but..

Linus

#2 lore raw
Quoted 2 lines · Linus Torvalds

Applied. Nothing needed but some time for me to look through it.

Well, I may have to revert that.

	mm/mm.o: In function `rw_swap_page':
	mm/mm.o(.text+0xaeb2): undefined reference to `suspend_device'

Please send me a fix asap.

Linus

#3 lore raw
In reply to Linus Torvalds

Hi!

Quoted 3 lines
Quoted 1 line

Applied. Nothing needed but some time for me to look through it.

Well, I may have to revert that.

Sorry about that. Safety check that should not be needed.

Quoted 4 lines
	mm/mm.o: In function `rw_swap_page':
	mm/mm.o(.text+0xaeb2): undefined reference to `suspend_device'

Please send me a fix asap.

This fixes it. I also broke compilation in ACPI but not SWSUSP case (but it ate disks before, so ... :-).

Pavel

mm/page_io.c +0 -4
@@ -86,15 +86,11 @@
  *  - it's marked as being swap-cache
  *  - it's associated with the swap inode
  */
-extern long suspend_device;
 void rw_swap_page(int rw, struct page *page)
 {
 	swp_entry_t entry;
 
 	entry.val = page->index;
-
-	if (suspend_device)
-		panic("I refuse to corrupt memory/swap.");
 
 	if (!PageLocked(page))
 		PAGE_BUG(page);
Signature · 2 lines
(about SSSCA) "I don't say this lightly.  However, I really think that the U.S.
no longer is classifiable as a democracy, but rather as a plutocracy." --hpa
#4 lore raw
In reply to Linus Torvalds

Hi!

Quoted 8 lines
Quoted 1 line

Applied. Nothing needed but some time for me to look through it.

Well, I may have to revert that.

	mm/mm.o: In function `rw_swap_page':
	mm/mm.o(.text+0xaeb2): undefined reference to `suspend_device'

Please send me a fix asap.

One more build fix, this time for !CONFIG_SOFTWARE_SUSPEND but CONFIG_ACPI:

I choosen this solution for now, as it is safest for ACPI people. I'll modify suspend.c so that freezing part can be included without whole suspend-to-disk support being included.
Pavel

drivers/acpi/acpi_system.c +1 -0
@@ -34,6 +34,7 @@
 #include <linux/sysrq.h>
 #include <linux/pm.h>
 #include <linux/device.h>
+#include <linux/suspend.h>
 #include <asm/uaccess.h>
 #include <asm/acpi.h>
 #include "acpi_bus.h"
include/linux/suspend.h +2 -0
@@ -60,6 +60,8 @@
 #define register_suspend_notifier(a)	do { } while(0)
 #define unregister_suspend_notifier(a)	do { } while(0)
 #define refrigerator(a)			do { BUG(); } while(0)
+#define freeze_processes()		do { panic("You need CONFIG_SOFTWARE_SUSPEND to do sleeps."); } while(0)
+#define thaw_processes()		do { } while(0)
 #endif
 
 #endif /* _LINUX_SWSUSP_H */
Signature · 2 lines
(about SSSCA) "I don't say this lightly.  However, I really think that the U.S.
no longer is classifiable as a democracy, but rather as a plutocracy." --hpa
#5 lore raw
In reply to Linus Torvalds

Uz.ytkownik Linus Torvalds napisa?:

Quoted 9 lines · Pavel Machek
Quoted 3 lines

Major parts are: process stopper, S3 specific code, S4 specific code. What can I do to make this applied?

Applied. Nothing needed but some time for me to look through it.

It still has a few too many #ifdef CONFIG_SUSPEND, and I get this feeling that the background deamons shouldn't need to do the "freeze()" by hand but simply be automatically frozen and thawed when they sleep by looking at the KERNTHREAD bit or something, but..

Oh and please reject the idea of compressing the pages you are writing to disk for the following reaons:

1. compression is not deterministic in terms of the possible space savings, you will still have to provide the required amount of space.

2. every compression algorithm has theoretical cases where the compression mechanism is actually increasing the space requirements.

3. Compressing around 360 Mbytes of data will take quite a lot of time.

4. Point 3 will make the CPU go high - not nice if the suspend happens in case of battery emergency...

Anyway it's time to repartition my notebook :-).

#6 lore raw
Quoted 6 lines · Martin Dalecki

Oh and please reject the idea of compressing the pages you are writing to disk for the following reaons:

1. compression is not deterministic in terms of the possible space savings, you will still have to provide the required amount of space.

Quoted 8 lines

2. every compression algorithm has theoretical cases where the compression mechanism is actually increasing the space requirements.

3. Compressing around 360 Mbytes of data will take quite a lot of time.

4. Point 3 will make the CPU go high - not nice if the suspend happens in case of battery emergency...

Compressing pages will speed up the process, and doing it on the fly will be more IO-bound than CPU-bound. I think trading some CPU power to gain HD time isn't so uninteresting.

Concerning point 2, you could always compress by chunks (say 1M) and take the compressed version only if it's smaller.

Xav

#7 lore raw
Quoted 4 lines · Xavier Bestel

Compressing pages will speed up the process, and doing it on the fly will be more IO-bound than CPU-bound. I think trading some CPU power to gain HD time isn't so uninteresting.

It's been a long time since disks were so slow that compression speeded things up.

_uncompression_ is often faster than disk speeds, but that's a fundamentally easier problem to solve. That means that a mostly read-only medium tends to work better with compressed contents, but in this case we have a write-once, read-once thing where compression is likely to lose.

Yes, laptops have slow disks, but they often have slow memory and seldom have the fastest CPU's available.

Linus

#8 lore raw
In reply to Linus Torvalds

Hi!

Quoted 6 lines
Quoted 6 lines
Quoted 2 lines

Major parts are: process stopper, S3 specific code, S4 specific code. What can I do to make this applied?

Applied. Nothing needed but some time for me to look through it.

Quoted 7 lines
Quoted 4 lines

It still has a few too many #ifdef CONFIG_SUSPEND, and I get this feeling that the background deamons shouldn't need to do the "freeze()" by hand but simply be automatically frozen and thawed when they sleep by looking at the KERNTHREAD bit or something, but..

Oh and please reject the idea of compressing the pages you are writing to disk for the following reaons:

[snip]

It probably was not going to be implemented anyway. It was meant to speed up suspend process, and yes simple compression probably could achieve that, but it is not worth the trouble, probably.

Quoted 6 lines

1. compression is not deterministic in terms of the possible space savings, you will still have to provide the required amount of space.

2. every compression algorithm has theoretical cases where the compression mechanism is actually increasing the space requirements.

Quoted 7 lines

3. Compressing around 360 Mbytes of data will take quite a lot of time.

4. Point 3 will make the CPU go high - not nice if the suspend happens in case of battery emergency...

Anyway it's time to repartition my notebook :-).

Yep. [Actually, if you have swap partition, you don't need to repartition anything.]
Pavel

Signature · 2 lines
Casualities in World Trade Center: ~3k dead inside the building,
cryptography in U.S.A. and free speech in Czech Republic.
#9 lore raw
In reply to Linus Torvalds

Uz.ytkownik Xavier Bestel napisa?:

Quoted 1 line

Compressing pages will speed up the process, and doing it on the fly

Did you ever in you life tar czvf ./some_dir and have a look at top?!

#10 lore raw
Quoted 5 lines · Martin Dalecki

Uz.ytkownik Xavier Bestel napisa?:

Quoted 1 line

Compressing pages will speed up the process, and doing it on the fly

Did you ever in you life tar czvf ./some_dir and have a look at top?!

lzo isn't such a CPU hog

#11 lore raw
In reply to Martin Dalecki
Quoted 5 lines · Martin Dalecki

Uz.ytkownik Xavier Bestel napisa?:

Quoted 1 line

Compressing pages will speed up the process, and doing it on the fly

Did you ever in you life tar czvf ./some_dir and have a look at top?!

I just tried to verify; on my laptop (Armada 1700 w/ pII 300 & IBM Travelstar) I have roughly 20% idle CPU during tar zcf. Laptop HDs are slow.

#12 lore raw
In reply to Linus Torvalds
Quoted 2 lines · Xavier Bestel

Concerning point 2, you could always compress by chunks (say 1M) and take the compressed version only if it's smaller.

which would mean you'd have to add a bit to determine if it's the compressed or the uncompressed chunk.. Which means you still have the increased maximum size... There really is no way of getting around the possible size increase.. (sorry to sound defeatist :) )

Signature · 3 lines
Frank v Waveren                                      Fingerprint: 0EDB 8787
fvw@[var.cx|stack.nl|dse.nl|chello.nl] ICQ#10074100     09B9 6EF5 6425 B855
Public key: hkp://wwwkeys.pgp.net/[email protected]            7179 3036 E136 B85D
#13 lore raw
In reply to Pavel Machek

Hi!

Quoted 4 lines
Quoted 2 lines

Major parts are: process stopper, S3 specific code, S4 specific code. What can I do to make this applied?

Applied. Nothing needed but some time for me to look through it.

Wow, good!

Quoted 2 lines

It still has a few too many #ifdef CONFIG_SUSPEND, and I get this feeling

I'll hunt them down.

Quoted 4 lines

that the background deamons shouldn't need to do the "freeze()" by hand
but simply be automatically frozen and thawed when they sleep by looking at the KERNTHREAD bit or something, but..

Do you think I should modify schedule() to do freezing automatically? I wanted to keep my hands off hot paths... I'd rather not do that.

I guess adding maybe_refrigerator() macro should solve that without introducing overhead in schedule().

Pavel

Signature · 2 lines
Casualities in World Trade Center: ~3k dead inside the building,
cryptography in U.S.A. and free speech in Czech Republic.
#14 lore raw
Quoted 2 lines · Pavel Machek

Do you think I should modify schedule() to do freezing automatically? I wanted to keep my hands off hot paths... I'd rather not do that.

No, I just suspect you could freeze them _while_ they sleep by just picking up their information from the normal save area.

Yeah, I know, Linux tends to save a lot of the process stuff implicitly on the stack, so maybe that ends up being harder than it sounds, and you've done it for other tasks with the signal handler code instead, but you _should_ be able to do it without any signal handler hackery by just saving off their kernel stack and the stuff in the thread structure.

That's just a gut feeling, not having actually looked at the real details.

Linus

#15 lore raw
In reply to Pavel Machek

Hi!

Quoted 11 lines
Quoted 2 lines

Do you think I should modify schedule() to do freezing automatically? I wanted to keep my hands off hot paths... I'd rather not do that.

No, I just suspect you could freeze them _while_ they sleep by just picking up their information from the normal save area.

Yeah, I know, Linux tends to save a lot of the process stuff implicitly on the stack, so maybe that ends up being harder than it sounds, and you've done it for other tasks with the signal handler code instead, but you _should_ be able to do it without any signal handler hackery by just saving off their kernel stack and the stuff in the thread structure.

I need to know more than they are sleeping. I also know they are sleeping *without holding any semaphores*. I need working system to be able to save state to disk. That's why I hacked it into signal handler.
Pavel

Signature · 2 lines
Casualities in World Trade Center: ~3k dead inside the building,
cryptography in U.S.A. and free speech in Czech Republic.
#16 lore raw
Quoted 5 lines · Pavel Machek

I need to know more than they are sleeping. I also know they are sleeping *without holding any semaphores*. I need working system to be able to save state to disk. That's why I hacked it into signal handler.

Sorry, I should have been more clear. I think the signal handler approach is fine for user processes, I was just wondering why you needed anything like that for kernel threads..

When a kernel thread is sleeping, I don't see that it has much state at
all: it will be re-started anyway on the next boot, and I don't see it having any "state".

(I didn't think it through enough - never mind saving the stack page, because I don't think there is anything at all interesting there to save).

Linus

#17 lore raw
In reply to Pavel Machek

Hi!

Quoted 5 lines
Quoted 4 lines

I need to know more than they are sleeping. I also know they are sleeping *without holding any semaphores*. I need working system to be able to save state to disk. That's why I hacked it into signal handler.

Quoted 7 lines

Sorry, I should have been more clear. I think the signal handler approach is fine for user processes, I was just wondering why you needed anything like that for kernel threads..

When a kernel thread is sleeping, I don't see that it has much state at
all: it will be re-started anyway on the next boot, and I don't see it having any "state".

If that kernel thread is sleeping because it waits for disk, it has some state, and I do not want to freeze it just now (because it might hold some lock). I only want to freeze them at safe point, and it seemed to me that for kernel threads it is easiest to mark safe points by hand.

Does it compile for you for now?
Pavel

Signature · 2 lines
Casualities in World Trade Center: ~3k dead inside the building,
cryptography in U.S.A. and free speech in Czech Republic.
#18 lore raw
In reply to Pavel Machek
Quoted 6 lines · Pavel Machek

Here's suspend-to-{RAM,disk} combined patch for
2.5.17. Suspend-to-disk is pretty stable and was tested in 2.4-ac. Suspend-to-RAM is little more experimental, but works for me, and is certainly better than disk-eating version currently in kernel.

+#define LINUX_REBOOT_CMD_SW_SUSPEND 0xD000FCE2

                                                      ^^^^

Are you sure it's system suspend and not system reset? ;-) Nice to see the Commodore 64 special numbers live on in Linux...

Gr{oetje,eeting}s,

Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- [email protected]

In personal conversations with technical people, I call myself a hacker. But when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds

#19 lore raw
In reply to Pavel Machek

Hi!

Quoted 9 lines
Quoted 6 lines

Here's suspend-to-{RAM,disk} combined patch for
2.5.17. Suspend-to-disk is pretty stable and was tested in 2.4-ac. Suspend-to-RAM is little more experimental, but works for me, and is certainly better than disk-eating version currently in kernel.

+#define LINUX_REBOOT_CMD_SW_SUSPEND 0xD000FCE2

                                                      ^^^^

Are you sure it's system suspend and not system reset? ;-) Nice to see the Commodore 64 special numbers live on in Linux...

Credits go to Gabor Kuti for this one... I did not know there's a joke hidden there ;-).
Pavel

Signature · 2 lines
Casualities in World Trade Center: ~3k dead inside the building,
cryptography in U.S.A. and free speech in Czech Republic.
#20 lore raw
In reply to Pavel Machek

On Wed, May 22, 2002 at 12:28:59AM +0200, Pavel Machek wrote:
...

I foresee trouble here; you're doing list_entry(list, struct page, list) on &area->free_list.

Quoted 8 lines · Pavel Machek
mm/page_alloc.c +8 -0
+#ifdef CONFIG_SOFTWARE_SUSPEND
+int is_head_of_free_region(struct page *p)
+{
+	pg_data_t *pgdat = pgdat_list;
+	unsigned type;
+	unsigned long flags;
+
+	for (type=0;type < MAX_NR_ZONES; type++) {

mm/page_alloc.c line 268

Quoted 10 lines
mm/page_alloc.c +10 -0
+		zone_t *zone = pgdat->node_zones + type;
+		int order = MAX_ORDER - 1;
+		free_area_t *area;
+		struct list_head *head, *curr;
+		spin_lock_irqsave(&zone->lock, flags);	/* Should not matter as we need quiescent system for suspend anyway, but... */
+
+		do {
+			area = zone->free_area + order;
+			head = &area->free_list;
+			curr = head;

^^^^^^^^^^^^
set right here

mm/page_alloc.c line 279

Quoted 11 lines · Pavel Machek
mm/page_alloc.c +11 -0
+
+			for(;;) {
+				if(!curr) {
+//					printk("FIXME: this should not happen but it does!!!");
+					break;
+				}
+				if(p != memlist_entry(curr, struct page, list)) {
+					curr = memlist_next(curr);
+					if (curr == head)
+						break;
+					continue;

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ deep trouble here and in the if ()

mm/page_alloc.c line 289

Quoted 10 lines · Pavel Machek
mm/page_alloc.c +10 -0
+				}
+				return 1 << order;
+			}
+		} while(order--);
+		spin_unlock_irqrestore(&zone->lock, flags);
+
+	}
+	return 0;
+}
+#endif /* CONFIG_SOFTWARE_SUSPEND */

The rest is okay...

I'd try writing it this way, and though I've not tested it, I've walked buddy lists a few times in the past week or two:

#ifdef CONFIG_SOFTWARE_SUSPEND
int is_head_of_free_region(struct page *page)

{
	zone_t *zone, *node_zones = pgdat_list->node_zones;
	unsigned long flags;

	for (zone = node_zones; zone - node_zones < MAX_NR_ZONES; ++zone) {
		int order;
		list_t *curr;

		/*
		 * Should not matter as we need quiescent system for
		 * suspend anyway, but...
		 */
		spin_lock_irqsave(&zone->lock, flags);
		for (order = MAX_ORDER - 1; order >= 0; --order)
			list_for_each(curr, &zone->free_area[order].free_list)
				if (page == list_entry(curr, struct page, list))
					return 1 << order;
		spin_unlock_irqrestore(&zone->lock, flags);

	}
	return 0;
}

#endif /* CONFIG_SOFTWARE_SUSPEND */

Cheers,
Bill

#21 lore raw
In reply to Pavel Machek

Hi!

Quoted 8 lines

The rest is okay...

I'd try writing it this way, and though I've not tested it, I've walked buddy lists a few times in the past week or two:

#ifdef CONFIG_SOFTWARE_SUSPEND
int is_head_of_free_region(struct page *page)

Quoted 15 lines
{
	zone_t *zone, *node_zones = pgdat_list->node_zones;
	unsigned long flags;

	for (zone = node_zones; zone - node_zones < MAX_NR_ZONES; ++zone) {
		int order;
		list_t *curr;

		/*
		 * Should not matter as we need quiescent system for
		 * suspend anyway, but...
		 */
		spin_lock_irqsave(&zone->lock, flags);
		for (order = MAX_ORDER - 1; order >= 0; --order)
			list_for_each(curr, &zone->free_area[order].free_list)

<== HERE ==>

Quoted 8 lines
				if (page == list_entry(curr, struct page, list))
					return 1 << order;
		spin_unlock_irqrestore(&zone->lock, flags);

	}
	return 0;
}

#endif /* CONFIG_SOFTWARE_SUSPEND */

I had to add

	if (!curr) break; 

to fix the oops. It now looks way nicer. Thanx.
Pavel

Signature · 2 lines
(about SSSCA) "I don't say this lightly.  However, I really think that the U.S.
no longer is classifiable as a democracy, but rather as a plutocracy." --hpa
#22 lore raw
In reply to Pavel Machek

Hi!

Quoted 28 lines

mm/page_alloc.c line 279

Quoted 11 lines
mm/page_alloc.c +11 -0
+
+			for(;;) {
+				if(!curr) {
+//					printk("FIXME: this should not happen but it does!!!");
+					break;
+				}
+				if(p != memlist_entry(curr, struct page, list)) {
+					curr = memlist_next(curr);
+					if (curr == head)
+						break;
+					continue;

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ deep trouble here and in the if ()

mm/page_alloc.c line 289

Quoted 10 lines · Pavel Machek
mm/page_alloc.c +10 -0
+				}
+				return 1 << order;
+			}
+		} while(order--);
+		spin_unlock_irqrestore(&zone->lock, flags);
+
+	}
+	return 0;
+}
+#endif /* CONFIG_SOFTWARE_SUSPEND */

The rest is okay...

Quoted 2 lines

I'd try writing it this way, and though I've not tested it, I've walked buddy lists a few times in the past week or two:

It dies with NULL pointer dereference. Perhaps "that should not happen but it does?".
Pavel

Signature · 2 lines
(about SSSCA) "I don't say this lightly.  However, I really think that the U.S.
no longer is classifiable as a democracy, but rather as a plutocracy." --hpa
#23 lore raw
In reply to Pavel Machek
Quoted 4 lines · Pavel Machek

Hi!
I had to add

	if (!curr) break; 

to fix the oops. It now looks way nicer. Thanx.

It's pretty odd that this happens to the buddy lists. I guess if it's needed as a stopgap measure, I can't complain too much, but I'd suspect something's corrupting it or you're catching a buddy list operation in progress. I might be interested in taking a stab at finding out where the corruption happens if you also think that's what's going on.

Cheers,
Bill

#24 lore raw
In reply to Pavel Machek

Hi!

Quoted 9 lines
Quoted 3 lines

I had to add

	if (!curr) break; 

to fix the oops. It now looks way nicer. Thanx.

It's pretty odd that this happens to the buddy lists. I guess if it's needed as a stopgap measure, I can't complain too much, but I'd suspect something's corrupting it or you're catching a buddy list operation in progress. I might be interested in taking a stab at finding out where the corruption happens if you also think that's what's going on.

I think it is not a coruption, but something like

"not all list are valid on non-himem machine", or something like that.

Pavel

Signature · 2 lines
Casualities in World Trade Center: ~3k dead inside the building,
cryptography in U.S.A. and free speech in Czech Republic.
#25 lore raw
In reply to Pavel Machek
Quoted 3 lines · Pavel Machek
Quoted 3 lines
Quoted 3 lines

I had to add

	if (!curr) break; 

to fix the oops. It now looks way nicer. Thanx.

Quoted 5 lines · At some point in the past, I wrote:
Quoted 5 lines

It's pretty odd that this happens to the buddy lists. I guess if it's needed as a stopgap measure, I can't complain too much, but I'd suspect something's corrupting it or you're catching a buddy list operation in progress. I might be interested in taking a stab at finding out where the corruption happens if you also think that's what's going on.

Quoted 3 lines · Pavel Machek

I think it is not a coruption, but something like
"not all list are valid on non-himem machine", or something like that.
Pavel

d'oh! Sorry about that, I forgot about free_area_init_core() not fixing up zeroed-out list_heads for ZONE_HIGHMEM. I don't see any harm in doing INIT_LIST_HEAD() on them in free_area_init_core(), and it would seem to fix this boundary case, no? Alternatively, highmem zones on non- highmem machines can be skipped by the caller as well. Do you think either of these might be good ideas, or is it going too far out of one's way for marginal code prettiness benefits?

Actually, I just thought of something that might be useful in addition, which is that it would probably only be necessary to search inside of page_zone(page), do you agree?

Cheers,
Bill

#26 lore raw
In reply to Pavel Machek

Hi!

Quoted 11 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines

I had to add

	if (!curr) break; 

to fix the oops. It now looks way nicer. Thanx.

Quoted 5 lines · At some point in the past, I wrote:
Quoted 5 lines

It's pretty odd that this happens to the buddy lists. I guess if it's needed as a stopgap measure, I can't complain too much, but I'd suspect something's corrupting it or you're catching a buddy list operation in progress. I might be interested in taking a stab at finding out where the corruption happens if you also think that's what's going on.

Quoted 8 lines
Quoted 3 lines · Pavel Machek

I think it is not a coruption, but something like
"not all list are valid on non-himem machine", or something like that.
Pavel

d'oh! Sorry about that, I forgot about free_area_init_core() not fixing up zeroed-out list_heads for ZONE_HIGHMEM. I don't see any harm in doing INIT_LIST_HEAD() on them in free_area_init_core(), and it

Doing INIT_LIST_HEAD() on them would be certainly cleaner...

Quoted 8 lines

seem to fix this boundary case, no? Alternatively, highmem zones on non- highmem machines can be skipped by the caller as well. Do you think either of these might be good ideas, or is it going too far out of one's way for marginal code prettiness benefits?

Actually, I just thought of something that might be useful in addition, which is that it would probably only be necessary to search inside of page_zone(page), do you agree?

Hey, speeding this up is not a top priority. But.. if we can make it simpler as well ... ;-).

#ifdef CONFIG_SOFTWARE_SUSPEND
int is_head_of_free_region(struct page *page)

{
        zone_t *zone = page_zone(page);
        unsigned long flags;
        int order;
        list_t *curr;

        /*
         * Should not matter as we need quiescent system for
         * suspend anyway, but...
         */
        spin_lock_irqsave(&zone->lock, flags);
        for (order = MAX_ORDER - 1; order >= 0; --order)
                list_for_each(curr, &zone->free_area[order].free_list){
                        if (!curr)
                                break;
                        if (page == list_entry(curr, struct page,list)) {
                                spin_unlock_irqrestore(&zone->lock,flags);
                                return 1 << order;
                        }
                }
        spin_unlock_irqrestore(&zone->lock, flags);
        return 0;
}

#endif /* CONFIG_SOFTWARE_SUSPEND */

Does that look okay to you?

Pavel

Signature · 2 lines
Casualities in World Trade Center: ~3k dead inside the building,
cryptography in U.S.A. and free speech in Czech Republic.
#27 lore raw
In reply to Pavel Machek
Quoted 3 lines · At some point in the past, I wrote:
Quoted 3 lines

Actually, I just thought of something that might be useful in addition, which is that it would probably only be necessary to search inside of page_zone(page), do you agree?

Quoted 2 lines · Pavel Machek

Hey, speeding this up is not a top priority. But.. if we can make it simpler as well ... ;-).

Yeah, system startup/shutdown isn't really something to optimize for aside from dodging some worst cases (which is not relevant here).

Quoted 25 lines · Pavel Machek

#ifdef CONFIG_SOFTWARE_SUSPEND
int is_head_of_free_region(struct page *page)

{
        zone_t *zone = page_zone(page);
        unsigned long flags;
        int order;
        list_t *curr;

        /*
         * Should not matter as we need quiescent system for
         * suspend anyway, but...
         */
        spin_lock_irqsave(&zone->lock, flags);
        for (order = MAX_ORDER - 1; order >= 0; --order)
                list_for_each(curr, &zone->free_area[order].free_list){
                        if (!curr)
                                break;
                        if (page == list_entry(curr, struct page,list)) {
                                spin_unlock_irqrestore(&zone->lock,flags);
                                return 1 << order;
                        }
                }
        spin_unlock_irqrestore(&zone->lock, flags);
        return 0;
}
Quoted 3 lines

#endif /* CONFIG_SOFTWARE_SUSPEND */
Does that look okay to you?
Pavel

This looks good, though OTOH if we have a page from a zone we should know the zone exists, and then maybe the if (!curr) check isn't needed. If that's the case the scary check that almost looks like defensive programming won't be needed at all. Also, is it always expected that this will be a free page?

Cheers,
Bill

#28 lore raw
In reply to Pavel Machek

Hi!

Quoted 42 lines
Quoted 3 lines
Quoted 3 lines

Actually, I just thought of something that might be useful in addition, which is that it would probably only be necessary to search inside of page_zone(page), do you agree?

Quoted 2 lines · Pavel Machek

Hey, speeding this up is not a top priority. But.. if we can make it simpler as well ... ;-).

Yeah, system startup/shutdown isn't really something to optimize for aside from dodging some worst cases (which is not relevant here).

Quoted 25 lines · Pavel Machek

#ifdef CONFIG_SOFTWARE_SUSPEND
int is_head_of_free_region(struct page *page)

{
        zone_t *zone = page_zone(page);
        unsigned long flags;
        int order;
        list_t *curr;

        /*
         * Should not matter as we need quiescent system for
         * suspend anyway, but...
         */
        spin_lock_irqsave(&zone->lock, flags);
        for (order = MAX_ORDER - 1; order >= 0; --order)
                list_for_each(curr, &zone->free_area[order].free_list){
                        if (!curr)
                                break;
                        if (page == list_entry(curr, struct page,list)) {
                                spin_unlock_irqrestore(&zone->lock,flags);
                                return 1 << order;
                        }
                }
        spin_unlock_irqrestore(&zone->lock, flags);
        return 0;
}
Quoted 3 lines

#endif /* CONFIG_SOFTWARE_SUSPEND */
Does that look okay to you?
Pavel

Quoted 3 lines

This looks good, though OTOH if we have a page from a zone we should know the zone exists, and then maybe the if (!curr) check isn't

Quoted 1 line
Quoted 1 line

needed. If that's

Good point. Killed.

Quoted 3 lines

the case the scary check that almost looks like defensive programming won't be needed at all. Also, is it always expected that this will be a free page?

No. It may be used page, too. That's why it is asking.
Pavel

Signature · 2 lines
Casualities in World Trade Center: ~3k dead inside the building,
cryptography in U.S.A. and free speech in Czech Republic.
#29 lore raw
In reply to Pavel Machek
Quoted 3 lines · At some point in the past, I wrote:
Quoted 3 lines

This looks good, though OTOH if we have a page from a zone we should know the zone exists, and then maybe the if (!curr) check isn't needed. If that's

Quoted 1 line · Pavel Machek

Good point. Killed.

Quoted 3 lines · At some point in the past, I wrote:
Quoted 3 lines

the case the scary check that almost looks like defensive programming won't be needed at all. Also, is it always expected that this will be a free page?

Quoted 2 lines · Pavel Machek

No. It may be used page, too. That's why it is asking.
Pavel

Aha, so it can't be a BUG() at the bottom then. Cool, this looks very nice!

Cheers,
Bill