Skip to contentSkip to conversation

swsusp-related fixes

PATCH Superseded Rafael J. Wysocki 2 participants 6 messages Jul 27, 2006

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

cover swsusp-related fixes

Hi,

The following patches fix some recently reported problems related to swsusp.

Greetings,
Rafael

#1 lore raw

PATCH 1/3 Disable CPU hotplug during suspend

The current suspend code has to be run on one CPU, so we use the CPU hotplug to take the non-boot CPUs offline on SMP machines. However, we should also make sure that these CPUs will not be enabled by someone else after we have disabled them.

The functions disable_nonboot_cpus() and enable_nonboot_cpus() are moved to kernel/cpu.c, because they now refer to some stuff in there that should better be static. Also it's better if disable_nonboot_cpus() returns an error instead of panicking if something goes wrong, and enable_nonboot_cpus() has no reason to panic(), because the CPUs may have been enabled by the userland before it tries to take them online.

---
 include/linux/cpu.h     |    8 +++
 include/linux/suspend.h |    8 ---
 kernel/cpu.c            |  109 +++++++++++++++++++++++++++++++++++++++++-------
 kernel/power/Makefile   |    2 
 kernel/power/disk.c     |    7 ++-
 kernel/power/main.c     |   10 +---
 kernel/power/smp.c      |   62 ---------------------------
 kernel/power/user.c     |   14 +++---
 8 files changed, 121 insertions(+), 99 deletions(-)
include/linux/suspend.h +0 -8
Index: linux-2.6.18-rc2/include/linux/suspend.h
===================================================================
@@ -57,14 +57,6 @@ static inline int software_suspend(void)
 }
 #endif /* CONFIG_PM */
 
-#ifdef CONFIG_SUSPEND_SMP
-extern void disable_nonboot_cpus(void);
-extern void enable_nonboot_cpus(void);
-#else
-static inline void disable_nonboot_cpus(void) {}
-static inline void enable_nonboot_cpus(void) {}
-#endif
-
 void save_processor_state(void);
 void restore_processor_state(void);
 struct saved_context;
kernel/power/smp.c deleted +0 -62

62 lines not on this page · raw message

kernel/power/disk.c +6 -1
Index: linux-2.6.18-rc2/kernel/power/disk.c
===================================================================
@@ -18,6 +18,7 @@
 #include <linux/fs.h>
 #include <linux/mount.h>
 #include <linux/pm.h>
+#include <linux/cpu.h>
 
 #include "power.h"
 
@@ -72,7 +73,10 @@ static int prepare_processes(void)
 	int error;
 
 	pm_prepare_console();
-	disable_nonboot_cpus();
+
+	error = disable_nonboot_cpus();
+	if (error)
+		goto enable_cpus;
 
 	if (freeze_processes()) {
 		error = -EBUSY;
@@ -84,6 +88,7 @@ static int prepare_processes(void)
 		return 0;
 thaw:
 	thaw_processes();
+enable_cpus:
 	enable_nonboot_cpus();
 	pm_restore_console();
 	return error;
kernel/power/main.c +4 -6
Index: linux-2.6.18-rc2/kernel/power/main.c
===================================================================
@@ -16,6 +16,7 @@
 #include <linux/init.h>
 #include <linux/pm.h>
 #include <linux/console.h>
+#include <linux/cpu.h>
 
 #include "power.h"
 
@@ -51,7 +52,7 @@ void pm_set_ops(struct pm_ops * ops)
 
 static int suspend_prepare(suspend_state_t state)
 {
-	int error = 0;
+	int error;
 	unsigned int free_pages;
 
 	if (!pm_ops || !pm_ops->enter)
@@ -59,12 +60,9 @@ static int suspend_prepare(suspend_state
 
 	pm_prepare_console();
 
-	disable_nonboot_cpus();
-
-	if (num_online_cpus() != 1) {
-		error = -EPERM;
+	error = disable_nonboot_cpus();
+	if (error)
 		goto Enable_cpu;
-	}
 
 	if (freeze_processes()) {
 		error = -EAGAIN;
kernel/power/user.c +9 -5
Index: linux-2.6.18-rc2/kernel/power/user.c
===================================================================
@@ -19,6 +19,7 @@
 #include <linux/swapops.h>
 #include <linux/pm.h>
 #include <linux/fs.h>
+#include <linux/cpu.h>
 
 #include <asm/uaccess.h>
 
@@ -139,12 +140,15 @@ static int snapshot_ioctl(struct inode *
 		if (data->frozen)
 			break;
 		down(&pm_sem);
-		disable_nonboot_cpus();
-		if (freeze_processes()) {
-			thaw_processes();
-			enable_nonboot_cpus();
-			error = -EBUSY;
+		error = disable_nonboot_cpus();
+		if (!error) {
+			error = freeze_processes();
+			if (error) {
+				thaw_processes();
+				error = -EBUSY;
+			}
 		}
+		enable_nonboot_cpus();
 		up(&pm_sem);
 		if (!error)
 			data->frozen = 1;
include/linux/cpu.h +8 -0
Index: linux-2.6.18-rc2/include/linux/cpu.h
===================================================================
@@ -95,4 +95,12 @@ int cpu_down(unsigned int cpu);
 static inline int cpu_is_offline(int cpu) { return 0; }
 #endif
 
+#ifdef CONFIG_SUSPEND_SMP
+extern int disable_nonboot_cpus(void);
+extern void enable_nonboot_cpus(void);
+#else
+static inline int disable_nonboot_cpus(void) { return 0; }
+static inline void enable_nonboot_cpus(void) {}
+#endif
+
 #endif /* _LINUX_CPU_H_ */
kernel/cpu.c +94 -15

148 lines not on this page · raw message

kernel/power/Makefile +0 -2
Index: linux-2.6.18-rc2/kernel/power/Makefile
===================================================================
@@ -7,6 +7,4 @@ obj-y				:= main.o process.o console.o
 obj-$(CONFIG_PM_LEGACY)		+= pm.o
 obj-$(CONFIG_SOFTWARE_SUSPEND)	+= swsusp.o disk.o snapshot.o swap.o user.o
 
-obj-$(CONFIG_SUSPEND_SMP)	+= smp.o
-
 obj-$(CONFIG_MAGIC_SYSRQ)	+= poweroff.o
#2 lore raw

PATCH 2/3 Make suspend possible with a traced process at a breakpoint

In reply to Rafael J. Wysocki

It should be possible to suspend, either to RAM or to disk, if there's a traced process that has just reached a breakpoint. However, this is a special case, because its parent process might have been frozen already and then we are unable to deliver the "freeze" signal to the traced process. If this happens, it's better to cancel the freezing of the traced process.

Ref. http://bugzilla.kernel.org/show_bug.cgi?id=6787

---
 include/linux/sched.h  |    8 ++++++++
 kernel/power/process.c |   26 ++++++++++++++++++--------
 2 files changed, 26 insertions(+), 8 deletions(-)
kernel/power/process.c +18 -8
Index: linux-2.6.18-rc2/kernel/power/process.c
===================================================================
@@ -66,13 +66,25 @@ static inline void freeze_process(struct
 	}
 }
 
+static void cancel_freezing(struct task_struct *p)
+{
+	unsigned long flags;
+
+	if (freezing(p)) {
+		pr_debug("  clean up: %s\n", p->comm);
+		do_not_freeze(p);
+		spin_lock_irqsave(&p->sighand->siglock, flags);
+		recalc_sigpending_tsk(p);
+		spin_unlock_irqrestore(&p->sighand->siglock, flags);
+	}
+}
+
 /* 0 = success, else # of processes that we failed to stop */
 int freeze_processes(void)
 {
 	int todo, nr_user, user_frozen;
 	unsigned long start_time;
 	struct task_struct *g, *p;
-	unsigned long flags;
 
 	printk( "Stopping tasks: " );
 	start_time = jiffies;
@@ -85,6 +97,10 @@ int freeze_processes(void)
 				continue;
 			if (frozen(p))
 				continue;
+			if (p->state == TASK_TRACED && frozen(p->parent)) {
+				cancel_freezing(p);
+				continue;
+			}
 			if (p->mm && !(p->flags & PF_BORROWED_MM)) {
 				/* The task is a user-space one.
 				 * Freeze it unless there's a vfork completion
@@ -126,13 +142,7 @@ int freeze_processes(void)
 		do_each_thread(g, p) {
 			if (freezeable(p) && !frozen(p))
 				printk(KERN_ERR "  %s\n", p->comm);
-			if (freezing(p)) {
-				pr_debug("  clean up: %s\n", p->comm);
-				p->flags &= ~PF_FREEZE;
-				spin_lock_irqsave(&p->sighand->siglock, flags);
-				recalc_sigpending_tsk(p);
-				spin_unlock_irqrestore(&p->sighand->siglock, flags);
-			}
+			cancel_freezing(p);
 		} while_each_thread(g, p);
 		read_unlock(&tasklist_lock);
 		return todo;
include/linux/sched.h +8 -0
Index: linux-2.6.18-rc2/include/linux/sched.h
===================================================================
@@ -1558,6 +1558,14 @@ static inline void freeze(struct task_st
 }
 
 /*
+ * Sometimes we may need to cancel the previous 'freeze' request
+ */
+static inline void do_not_freeze(struct task_struct *p)
+{
+	p->flags &= ~PF_FREEZE;
+}
+
+/*
  * Wake up a frozen process
  */
 static inline int thaw_process(struct task_struct *p)
#3 lore raw

Re: [PATCH 2/3] Make suspend possible with a traced process at a breakpoint

Hi!

Quoted 5 lines

It should be possible to suspend, either to RAM or to disk, if there's a traced process that has just reached a breakpoint. However, this is a special case, because its parent process might have been frozen already and then we are unable to deliver the "freeze" signal to the traced process. If this happens, it's better to cancel the freezing of the traced process.

Thanks for debugging this.

Review int freeze_processes(void) · line 103

Quoted 8 lines
(quoted diff) +4 -0
@@ -85,6 +97,10 @@ int freeze_processes(void)
 				continue;
 			if (frozen(p))
 				continue;
+			if (p->state == TASK_TRACED && frozen(p->parent)) {
+				cancel_freezing(p);
+				continue;
+			}

Could we just add line to freezeable, like this?

...no, this is probably racy, because freezeable() should not change while we are thawing other processes... I was hoping for one-liner...

kernel/power/process.c +1 -0
index b2a5f67..13dceb8 100644
@@ -26,6 +26,7 @@ static inline int freezeable(struct task
 	    (p->flags & PF_NOFREEZE) ||
 	    (p->exit_state == EXIT_ZOMBIE) ||
 	    (p->exit_state == EXIT_DEAD) ||
+	    ((p->exit_state == TASK_TRACED) && frozen(p->parent)) ||
 	    (p->state == TASK_STOPPED))
 		return 0;
 	return 1;

...okay, so ACK on your patch.
Pavel

Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
#4 lore raw

PATCH 3/3 Allow cifsd to suspend if connection is lost

In reply to Rafael J. Wysocki

Make cifsd allow us to suspend if it has lost the connection with the server.

Ref. http://bugzilla.kernel.org/show_bug.cgi?id=6811

---
 fs/cifs/connect.c |    1 +
 1 file changed, 1 insertion(+)
fs/cifs/connect.c +1 -0
Index: linux-2.6.18-rc2/fs/cifs/connect.c
===================================================================
@@ -182,6 +182,7 @@ cifs_reconnect(struct TCP_Server_Info *s
 
 	while ((server->tcpStatus != CifsExiting) && (server->tcpStatus != CifsGood))
 	{
+		try_to_freeze();
 		if(server->protocolType == IPV6) {
 			rc = ipv6_connect(&server->addr.sockAddr6,&server->ssocket);
 		} else {