Skip to contentSkip to conversation

Fix Freezer related races.

RFC In review Gautham R Shenoy 8 participants 60 messages Apr 19, 2007 – Apr 26, 2007 5 messages not in the archive

Subsystems (23)
Arm (32-bit) SCSI USB PowerPC +19 more subsystems
lore
Share & tools
lore
#0 lore raw

cover Fix Freezer related races.

Hello Everyone,
Oleg had pointed out some subtle races which could lead to the failure of the process freezer in the patchset which I had posted earlier.

A couple of these problems seem to be present in the latest -mm(2.6.21-rc6-mm1).

This is an attempt to fix them.

Awaiting you feedback,
Regards
gautham.

Signature · 5 lines
Gautham R Shenoy
Linux Technology Center
IBM India.
"Freedom comes with a price tag of responsibility, which is still a bargain,
because Freedom is priceless!"
#1 lore raw

RFC PATCH 1/2 Fix PF_NOFREEZE and freezeable race

This patch fixes the race pointed out by Oleg Nesterov.

* Freezer marks a thread as freezeable.
* The thread now marks itself PF_NOFREEZE causing it to freeze on calling try_to_freeze(). Thus the task is frozen, even though it doesn't want to.
* Subsequent thaw_processes() will also fail to thaw the task since it is marked PF_NOFREEZE.

Avoid this problem by checking the current task's PF_NOFREEZE status in the refrigerator before marking current as frozen.

---
 kernel/power/process.c |    9 +++++++++
 1 file changed, 9 insertions(+)
kernel/power/process.c +9 -0
Index: linux-2.6.21-rc6/kernel/power/process.c
===================================================================
@@ -41,6 +41,15 @@ void refrigerator(void)
 
 	task_lock(current);
 	if (freezing(current)) {
+		/* check if we had marked ourself PF_NOFREEZE
+		 * *after* the freezer did the freezeable() check
+		 * on us.
+		 */
+		if (current->flags & PF_NOFREEZE) {
+			clear_tsk_thread_flag(current, TIF_FREEZE);
+			task_unlock(current);
+			return;
+		}
 		frozen_process(current);
 		task_unlock(current);
 	} else {
Signature · 5 lines
Gautham R Shenoy
Linux Technology Center
IBM India.
"Freedom comes with a price tag of responsibility, which is still a bargain,
because Freedom is priceless!"
#2 lore raw

Re: [RFC PATCH 1/2] Fix PF_NOFREEZE and freezeable race

Hi,

Quoted 9 lines · Gautham R Shenoy

This patch fixes the race pointed out by Oleg Nesterov.

* Freezer marks a thread as freezeable.
* The thread now marks itself PF_NOFREEZE causing it to freeze on calling try_to_freeze(). Thus the task is frozen, even though it doesn't want to.
* Subsequent thaw_processes() will also fail to thaw the task since it is marked PF_NOFREEZE.

Quoted 4 lines

Avoid this problem by checking the current task's PF_NOFREEZE status in the refrigerator before marking current as frozen.

Looks good, although I'm not sure if we don't need to call recalc_sigpending() for tasks that turn out to be PF_NOFREEZE.

Greetings,
Rafael

#3 lore raw

Re: [RFC PATCH 1/2] Fix PF_NOFREEZE and freezeable race

Quoted 11 lines · Rafael J. Wysocki
Quoted 9 lines · Gautham R Shenoy

This patch fixes the race pointed out by Oleg Nesterov.

* Freezer marks a thread as freezeable.
* The thread now marks itself PF_NOFREEZE causing it to freeze on calling try_to_freeze(). Thus the task is frozen, even though it doesn't want to.
* Subsequent thaw_processes() will also fail to thaw the task since it is marked PF_NOFREEZE.

Quoted 7 lines
Quoted 4 lines

Avoid this problem by checking the current task's PF_NOFREEZE status in the refrigerator before marking current as frozen.

Looks good, although I'm not sure if we don't need to call recalc_sigpending() for tasks that turn out to be PF_NOFREEZE.

I agree, we should clear TIF_SIGPENDING. It is not so critical for user-space tasks, but for the kernel thread it may remain pending forever, causing subtle failures.

Gautham, isn't it possible to make a more simpler patch ? Just add PF_NOFREEZE check to frozen_process,

	static inline void frozen_process(struct task_struct *p)
	{
		if (!unlikely(current->flags & PF_NOFREEZE)) {
			p->flags |= PF_FROZEN;
			wmb();
		}
		clear_tsk_thread_flag(p, TIF_FREEZE);
	}

No?

Oleg.

#4 lore raw

Re: [RFC PATCH 1/2] Fix PF_NOFREEZE and freezeable race

Quoted 36 lines · Oleg Nesterov
Quoted 11 lines · Rafael J. Wysocki
Quoted 9 lines · Gautham R Shenoy

This patch fixes the race pointed out by Oleg Nesterov.

* Freezer marks a thread as freezeable.
* The thread now marks itself PF_NOFREEZE causing it to freeze on calling try_to_freeze(). Thus the task is frozen, even though it doesn't want to.
* Subsequent thaw_processes() will also fail to thaw the task since it is marked PF_NOFREEZE.

Quoted 7 lines
Quoted 4 lines

Avoid this problem by checking the current task's PF_NOFREEZE status in the refrigerator before marking current as frozen.

Looks good, although I'm not sure if we don't need to call recalc_sigpending() for tasks that turn out to be PF_NOFREEZE.

I agree, we should clear TIF_SIGPENDING. It is not so critical for user-space tasks, but for the kernel thread it may remain pending forever, causing subtle failures.

Gautham, isn't it possible to make a more simpler patch ? Just add PF_NOFREEZE check to frozen_process,

	static inline void frozen_process(struct task_struct *p)
	{
		if (!unlikely(current->flags & PF_NOFREEZE)) {
			p->flags |= PF_FROZEN;
			wmb();
		}
		clear_tsk_thread_flag(p, TIF_FREEZE);
	}
Quoted 1 line

No?

Actually yes. The idea anyway was to check one last time before declaring ourselves as frozen. So I thought the best place was inside refrigerator since we are already holding the task_lock there.
I wasn't too sure about calling recalc_sigpending(), but now that you mention it, I agree, this would be a nicer way to do it.

Btw, since frozen_process is currently being called only from refrigerator, I am wondering if we still need the struct task_struct *p parameter there. It's very unlikely that some other task would mark a particular task as frozen. No?

Anyways, Andrew, Could you please replace the earlier sent patch titled "fix_pf_nofreeze_and_freezeable_race.patch" with the following one?

Thanks and Regards
gautham.

-->
This patch fixes the race pointed out by Oleg Nesterov.

* Freezer marks a thread as freezeable.
* The thread now marks itself PF_NOFREEZE causing it to freeze on calling try_to_freeze(). Thus the task is frozen, even though it doesn't want to.
* Subsequent thaw_processes() will also fail to thaw the task since it is marked PF_NOFREEZE.

Avoid this problem by checking the task's PF_NOFREEZE status in frozen_processes() before marking the task as frozen.

---
 include/linux/freezer.h |    6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)
include/linux/freezer.h +4 -2
Index: linux-2.6.21-rc6/include/linux/freezer.h
===================================================================
@@ -57,8 +57,10 @@ static inline int thaw_process(struct ta
  */
 static inline void frozen_process(struct task_struct *p)
 {
-	p->flags |= PF_FROZEN;
-	wmb();
+	if (!unlikely(p->flags & PF_NOFREEZE)) {
+		p->flags |= PF_FROZEN;
+		wmb();
+	}
 	clear_tsk_thread_flag(p, TIF_FREEZE);
 }
 
Signature · 5 lines
Gautham R Shenoy
Linux Technology Center
IBM India.
"Freedom comes with a price tag of responsibility, which is still a bargain,
because Freedom is priceless!"
#5 lore raw

Re: [RFC PATCH 1/2] Fix PF_NOFREEZE and freezeable race

Quoted 16 lines · Gautham R Shenoy
Quoted 4 lines · Oleg Nesterov

Gautham, isn't it possible to make a more simpler patch ? Just add PF_NOFREEZE check to frozen_process,

Quoted 9 lines
	static inline void frozen_process(struct task_struct *p)
	{
		if (!unlikely(current->flags & PF_NOFREEZE)) {
			p->flags |= PF_FROZEN;
			wmb();
		}
		clear_tsk_thread_flag(p, TIF_FREEZE);
	}
Quoted 4 lines

Btw, since frozen_process is currently being called only from refrigerator, I am wondering if we still need the struct task_struct *p parameter there. It's very unlikely that some other task would mark a particular task as frozen. No?

I agree. Only "current" can set PF_FROZEN anyway. I also think it is better to move this function into kernel/power/process.c, no need to export it in freezer.h. It is special, should be called from refrigerator() with task_lock() held.

Quoted 9 lines

Anyways, Andrew, Could you please replace the earlier sent patch titled "fix_pf_nofreeze_and_freezeable_race.patch" with the following one?

...

include/linux/freezer.h +0 -0
@@ -57,8 +57,10 @@ static inline int thaw_process(struct ta
  */

include/linux/freezer.h static inline int thaw_process(struct ta · line 65

Quoted 10 lines
include/linux/freezer.h +4 -2
 static inline void frozen_process(struct task_struct *p)
 {
-	p->flags |= PF_FROZEN;
-	wmb();
+	if (!unlikely(p->flags & PF_NOFREEZE)) {
+		p->flags |= PF_FROZEN;
+		wmb();
+	}
 	clear_tsk_thread_flag(p, TIF_FREEZE);
 }

On the second thought, this patch doesn't help if the thread never calls try_to_freeze() exactly because it marks itself PF_NOFREEZE.

Perhaps it is better to cancel freezing in soon-to-be-introduced freezer_exempt().

In any case I agree, this one is better than current
fix_pf_nofreeze_and_freezeable_race.patch

Oleg.

A message that is not in the archive (sent off-list or not yet received): [email protected]

A message that is not in the archive (sent off-list or not yet received): [email protected]

A message that is not in the archive (sent off-list or not yet received): [email protected]

A message that is not in the archive (sent off-list or not yet received): [email protected]

A message that is not in the archive (sent off-list or not yet received): [email protected]

#11 lore raw

PATCH 1/1 Move frozen_process() to kernel/power/process.c

Hi Andrew,
Here's the patch against 2.6.21-rc7-mm2.

-->

Other than refrigerator, no one else calls frozen_process(). So move it from include/linux/freezer.h to kernel/power/process.c.

Also, since a task can be marked frozen by itself, we don't need to pass the (struct task_struct *p) parameter to frozen_process().

---
 include/linux/freezer.h |   12 ------------
 kernel/power/process.c  |   14 +++++++++++++-
 2 files changed, 13 insertions(+), 13 deletions(-)
include/linux/freezer.h +0 -12
Index: linux-2.6.21-rc7/include/linux/freezer.h
===================================================================
@@ -58,18 +58,6 @@ static inline int thaw_process(struct ta
 	return 0;
 }
 
-/*
- * freezing is complete, mark process as frozen
- */
-static inline void frozen_process(struct task_struct *p)
-{
-	if (!unlikely(p->flags & PF_NOFREEZE)) {
-		p->flags |= PF_FROZEN;
-		wmb();
-	}
-	clear_tsk_thread_flag(p, TIF_FREEZE);
-}
-
 extern void refrigerator(void);
 extern int freeze_processes(void);
 extern void thaw_processes(void);
kernel/power/process.c +13 -1
Index: linux-2.6.21-rc7/kernel/power/process.c
===================================================================
@@ -32,6 +32,18 @@ static inline int freezeable(struct task
 	return 1;
 }
 
+/*
+ * freezing is complete, mark current process as frozen
+ */
+static inline void frozen_process(void)
+{
+	if (!unlikely(current->flags & PF_NOFREEZE)) {
+		current->flags |= PF_FROZEN;
+		wmb();
+	}
+	clear_tsk_thread_flag(current, TIF_FREEZE);
+}
+
 /* Refrigerator is place where frozen processes are stored :-). */
 void refrigerator(void)
 {
@@ -41,7 +53,7 @@ void refrigerator(void)
 
 	task_lock(current);
 	if (freezing(current)) {
-		frozen_process(current);
+		frozen_process();
 		task_unlock(current);
 	} else {
 		task_unlock(current);
Signature · 5 lines
Gautham R Shenoy
Linux Technology Center
IBM India.
"Freedom comes with a price tag of responsibility, which is still a bargain,
because Freedom is priceless!"
#12 lore raw

Re: [PATCH -mm] Move frozen_process() to kernel/power/process.c

Quoted 3 lines · Gautham R Shenoy

Other than refrigerator, no one else calls frozen_process(). So move it from include/linux/freezer.h to kernel/power/process.c.

Could you also remove this

	static inline void frozen_process(struct task_struct *p) { BUG(); }

from freezer.h ?

Oleg.

#13 lore raw

Re: [PATCH -mm] Move frozen_process() to kernel/power/process.c

Quoted 10 lines · Oleg Nesterov
Quoted 3 lines · Gautham R Shenoy

Other than refrigerator, no one else calls frozen_process(). So move it from include/linux/freezer.h to kernel/power/process.c.

Could you also remove this

	static inline void frozen_process(struct task_struct *p) { BUG(); }

from freezer.h ?

Sure. Here's a more complete patch.

Thanks and Regards
gautham.

-->
Other than refrigerator, no one else calls frozen_process(). So move it from include/linux/freezer.h to kernel/power/process.c.

Also, since a task can be marked frozen by itself, we don't need to pass the (struct task_struct *p) parameter to frozen_process().

---
 include/linux/freezer.h |   13 -------------
 kernel/power/process.c  |   14 +++++++++++++-
 2 files changed, 13 insertions(+), 14 deletions(-)
include/linux/freezer.h +0 -13
Index: linux-2.6.21-rc7/include/linux/freezer.h
===================================================================
@@ -58,18 +58,6 @@ static inline int thaw_process(struct ta
 	return 0;
 }
 
-/*
- * freezing is complete, mark process as frozen
- */
-static inline void frozen_process(struct task_struct *p)
-{
-	if (!unlikely(p->flags & PF_NOFREEZE)) {
-		p->flags |= PF_FROZEN;
-		wmb();
-	}
-	clear_tsk_thread_flag(p, TIF_FREEZE);
-}
-
 extern void refrigerator(void);
 extern int freeze_processes(void);
 extern void thaw_processes(void);
@@ -132,7 +120,6 @@ static inline int frozen(struct task_str
 static inline int freezing(struct task_struct *p) { return 0; }
 static inline void freeze(struct task_struct *p) { BUG(); }
 static inline int thaw_process(struct task_struct *p) { return 1; }
-static inline void frozen_process(struct task_struct *p) { BUG(); }
 
 static inline void refrigerator(void) {}
 static inline int freeze_processes(void) { BUG(); return 0; }
kernel/power/process.c +13 -1
Index: linux-2.6.21-rc7/kernel/power/process.c
===================================================================
@@ -32,6 +32,18 @@ static inline int freezeable(struct task
 	return 1;
 }
 
+/*
+ * freezing is complete, mark current process as frozen
+ */
+static inline void frozen_process(void)
+{
+	if (!unlikely(current->flags & PF_NOFREEZE)) {
+		current->flags |= PF_FROZEN;
+		wmb();
+	}
+	clear_tsk_thread_flag(current, TIF_FREEZE);
+}
+
 /* Refrigerator is place where frozen processes are stored :-). */
 void refrigerator(void)
 {
@@ -41,7 +53,7 @@ void refrigerator(void)
 
 	task_lock(current);
 	if (freezing(current)) {
-		frozen_process(current);
+		frozen_process();
 		task_unlock(current);
 	} else {
 		task_unlock(current);
Signature · 5 lines
Gautham R Shenoy
Linux Technology Center
IBM India.
"Freedom comes with a price tag of responsibility, which is still a bargain,
because Freedom is priceless!"
#14 lore raw

RFC 2/2 Fix freezer-kthread_stop race

In reply to Gautham R Shenoy

Threads which wait for completion on a frozen thread might result in causing the freezer to fail, if the waiting thread is freezeable.

There are some well known cases where it's preferable to temporarily thaw the frozen process, finish the wait for completion and allow both the processes to call try_to_freeze.

kthread_stop is one such case. flush_workqueue might be another.

This patch attempts to address such a situation with a fix for kthread_stop.

Strictly experimental. Compile tested on i386.

---
 include/asm-arm/thread_info.h      |    2 
 include/asm-blackfin/thread_info.h |    4 +
 include/asm-frv/thread_info.h      |    4 +
 include/asm-i386/thread_info.h     |    4 +
 include/asm-ia64/thread_info.h     |    4 +
 include/asm-mips/thread_info.h     |    2 
 include/asm-powerpc/thread_info.h  |    4 +
 include/asm-sh/thread_info.h       |    2 
 include/asm-x86_64/thread_info.h   |    4 +
 include/linux/freezer.h            |    4 +
 kernel/kthread.c                   |    4 +
 kernel/power/process.c             |   81 ++++++++++++++++++++++++++++++++++++-
 12 files changed, 118 insertions(+), 1 deletion(-)
kernel/power/process.c +80 -1
Index: linux-2.6.21-rc6/kernel/power/process.c
===================================================================
@@ -23,6 +23,16 @@
 #define FREEZER_KERNEL_THREADS 0
 #define FREEZER_USER_SPACE 1
 
+struct freezer_status_struct {
+	spinlock_t lock;
+	int count;
+};
+
+static struct freezer_status_struct freezer_status = {
+				.lock = SPIN_LOCK_UNLOCKED,
+				.count = 0,
+			};
+
 static inline int freezeable(struct task_struct * p)
 {
 	if ((p == current) ||
@@ -45,7 +55,8 @@ void refrigerator(void)
 		 * *after* the freezer did the freezeable() check
 		 * on us.
 		 */
-		if (current->flags & PF_NOFREEZE) {
+		if ((current->flags & PF_NOFREEZE) ||
+		    test_tsk_thread_flag(current, TIF_FREEZER_HELD)) {
 			clear_tsk_thread_flag(current, TIF_FREEZE);
 			task_unlock(current);
 			return;
@@ -63,12 +74,16 @@ void refrigerator(void)
 	recalc_sigpending(); /* We sent fake signal, clean it up */
 	spin_unlock_irq(&current->sighand->siglock);
 
+	task_lock(current);
 	for (;;) {
 		set_current_state(TASK_UNINTERRUPTIBLE);
 		if (!frozen(current))
 			break;
+		task_unlock(current);
 		schedule();
+		task_lock(current);
 	}
+	task_unlock(current);
 	pr_debug("%s left refrigerator\n", current->comm);
 	current->state = save;
 }
@@ -114,6 +129,47 @@ static inline int is_user_space(struct t
 	return ret;
 }
 
+/*
+ * Delay the freezer from declaring the system as frozen,
+ * if it is not frozen already.
+ *
+ * Usage:
+ * int result = hold_freezer_for_task(p);
+ * wait_for_completion(something_which_p_completes);
+ * release_freezer(p, result);
+ */
+
+int hold_freezer_for_task(struct task_struct *p)
+{
+	int ret = 0;
+	spin_lock(&freezer_status.lock);
+	if (freezer_status.count >= 0)
+	{
+		set_tsk_thread_flag(p, TIF_FREEZER_HELD);
+		thaw_process(p);
+		freezer_status.count++;
+		ret = 1;
+	}
+	spin_unlock(&freezer_status.lock);
+
+	return ret;
+}
+
+/*
+ * Allow freezer to function normally as before.
+ * Usage: See the comment above definition of hold_freezer_for_task()
+ */
+void release_freezer(struct task_struct *p, int result)
+{
+	if (result) {
+		spin_lock(&freezer_status.lock);
+		BUG_ON(freezer_status.count <= 0);
+		clear_tsk_thread_flag(p, TIF_FREEZER_HELD);
+		freezer_status.count--;
+		spin_unlock(&freezer_status.lock);
+	}
+
+}
 static unsigned int try_to_freeze_tasks(int freeze_user_space)
 {
 	struct task_struct *g, *p;
@@ -146,6 +202,22 @@ static unsigned int try_to_freeze_tasks(
 		yield();			/* Yield is okay here */
 		if (todo && time_after(jiffies, end_time))
 			break;
+
+		if (!freeze_user_space && !todo) {
+			spin_lock(&freezer_status.lock);
+			if (freezer_status.count == 0)
+				freezer_status.count--;
+			else {
+				spin_unlock(&freezer_status.lock);
+				/* check once more for any unfrozen
+				 * tasks. someone might have thawed
+				 * a task temporarily.
+				 */
+				continue;
+			}
+			spin_unlock(&freezer_status.lock);
+		}
+
 	} while (todo);
 
 	if (todo) {
@@ -219,6 +291,13 @@ static void thaw_tasks(int thaw_user_spa
 		thaw_process(p);
 	} while_each_thread(g, p);
 	read_unlock(&tasklist_lock);
+
+	if (thaw_user_space) {
+		spin_lock(&freezer_status.lock);
+			if (freezer_status.count < 0)
+				freezer_status.count++;
+		spin_unlock(&freezer_status.lock);
+	}
 }
 
 void thaw_processes(void)
include/linux/freezer.h +4 -0
Index: linux-2.6.21-rc6/include/linux/freezer.h
===================================================================
@@ -65,6 +65,8 @@ static inline void frozen_process(struct
 extern void refrigerator(void);
 extern int freeze_processes(void);
 extern void thaw_processes(void);
+extern int hold_freezer_for_task(struct task_struct *p);
+extern void release_freezer(struct task_struct *p, int result);
 
 static inline int try_to_freeze(void)
 {
@@ -125,6 +127,8 @@ static inline int freezing(struct task_s
 static inline void freeze(struct task_struct *p) { BUG(); }
 static inline int thaw_process(struct task_struct *p) { return 1; }
 static inline void frozen_process(struct task_struct *p) { BUG(); }
+static inline int hold_freezer_for_task(struct task_struct *p) { return 0;}
+static inline void release_freezer(struct task_struct *p, int result) {}
 
 static inline void refrigerator(void) {}
 static inline int freeze_processes(void) { BUG(); return 0; }
kernel/kthread.c +4 -0
Index: linux-2.6.21-rc6/kernel/kthread.c
===================================================================
@@ -14,6 +14,7 @@
 #include <linux/module.h>
 #include <linux/mutex.h>
 #include <asm/semaphore.h>
+#include <linux/freezer.h>
 
 /*
  * We dont want to execute off keventd since it might
@@ -220,6 +221,7 @@ EXPORT_SYMBOL(kthread_bind);
 int kthread_stop(struct task_struct *k)
 {
 	int ret;
+	int freezer_is_held;
 
 	mutex_lock(&kthread_stop_lock);
 
@@ -236,7 +238,9 @@ int kthread_stop(struct task_struct *k)
 	put_task_struct(k);
 
 	/* Once it dies, reset stop ptr, gather result and we're done. */
+	freezer_is_held = hold_freezer_for_task(k);
 	wait_for_completion(&kthread_stop_info.done);
+	release_freezer(k, freezer_is_held);
 	kthread_stop_info.k = NULL;
 	ret = kthread_stop_info.err;
 	mutex_unlock(&kthread_stop_lock);
include/asm-arm/thread_info.h +2 -0
Index: linux-2.6.21-rc6/include/asm-arm/thread_info.h
===================================================================
@@ -148,6 +148,7 @@ extern void iwmmxt_task_switch(struct th
 #define TIF_USING_IWMMXT	17
 #define TIF_MEMDIE		18
 #define TIF_FREEZE		19
+#define TIF_FREEZER_HELD	20
 
 #define _TIF_NOTIFY_RESUME	(1 << TIF_NOTIFY_RESUME)
 #define _TIF_SIGPENDING		(1 << TIF_SIGPENDING)
@@ -156,6 +157,7 @@ extern void iwmmxt_task_switch(struct th
 #define _TIF_POLLING_NRFLAG	(1 << TIF_POLLING_NRFLAG)
 #define _TIF_USING_IWMMXT	(1 << TIF_USING_IWMMXT)
 #define _TIF_FREEZE		(1 << TIF_FREEZE)
+#define _TIF_FREEZER_HELD	(1 << TIF_FREEZER_HELD)
 
 /*
  * Change these and you break ASM code in entry-common.S
include/asm-blackfin/thread_info.h +4 -0
Index: linux-2.6.21-rc6/include/asm-blackfin/thread_info.h
===================================================================
@@ -127,6 +127,9 @@ static inline struct thread_info *curren
 #define TIF_RESTORE_SIGMASK	6	/* restore signal mask in do_signal() */
 #define TIF_FREEZE              7       /* is freezing for suspend */
 #define TIF_SINGLESTEP		8       /* restore singlestep on return to user mode */
+#define TIF_FREEZER_HELD	9	/* Thread is temporarily holding up
+					 * the process freezer
+					 */
 
 /* as above, but as bit values */
 #define _TIF_SYSCALL_TRACE	(1<<TIF_SYSCALL_TRACE)
@@ -137,6 +140,7 @@ static inline struct thread_info *curren
 #define _TIF_RESTORE_SIGMASK	(1<<TIF_RESTORE_SIGMASK)
 #define _TIF_FREEZE             (1<<TIF_FREEZE)
 #define _TIF_SINGLESTEP		(1<<TIF_SINGLESTEP)
+#define _TIF_FREEZER_HELD	(1<<TIF_FREEZER_HELD)
 
 #define _TIF_WORK_MASK		0x0000FFFE	/* work to do on interrupt/exception return */
 
include/asm-frv/thread_info.h +4 -0
Index: linux-2.6.21-rc6/include/asm-frv/thread_info.h
===================================================================
@@ -117,6 +117,9 @@ register struct thread_info *__current_t
 #define TIF_POLLING_NRFLAG	16	/* true if poll_idle() is polling TIF_NEED_RESCHED */
 #define TIF_MEMDIE		17	/* OOM killer killed process */
 #define TIF_FREEZE		18	/* freezing for suspend */
+#define TIF_FREEZER_HELD	19	/* Thread is holding up freezer
+					 * temporarily.
+					 */
 
 #define _TIF_SYSCALL_TRACE	(1 << TIF_SYSCALL_TRACE)
 #define _TIF_NOTIFY_RESUME	(1 << TIF_NOTIFY_RESUME)
@@ -127,6 +130,7 @@ register struct thread_info *__current_t
 #define _TIF_RESTORE_SIGMASK	(1 << TIF_RESTORE_SIGMASK)
 #define _TIF_POLLING_NRFLAG	(1 << TIF_POLLING_NRFLAG)
 #define _TIF_FREEZE		(1 << TIF_FREEZE)
+#define _TIF_FREEZER_HELD	(1 << TIF_FREEZER_HELD)
 
 #define _TIF_WORK_MASK		0x0000FFFE	/* work to do on interrupt/exception return */
 #define _TIF_ALLWORK_MASK	0x0000FFFF	/* work to do on any return to u-space */
include/asm-i386/thread_info.h +4 -0
Index: linux-2.6.21-rc6/include/asm-i386/thread_info.h
===================================================================
@@ -137,6 +137,9 @@ static inline struct thread_info *curren
 #define TIF_IO_BITMAP		18	/* uses I/O bitmap */
 #define TIF_FREEZE		19	/* is freezing for suspend */
 #define TIF_FORCED_TF		20	/* true if TF in eflags artificially */
+#define TIF_FREEZER_HELD	21	/* is temporarily holding up process
+					 * freezer
+					 */
 
 #define _TIF_SYSCALL_TRACE	(1<<TIF_SYSCALL_TRACE)
 #define _TIF_NOTIFY_RESUME	(1<<TIF_NOTIFY_RESUME)
@@ -151,6 +154,7 @@ static inline struct thread_info *curren
 #define _TIF_IO_BITMAP		(1<<TIF_IO_BITMAP)
 #define _TIF_FREEZE		(1<<TIF_FREEZE)
 #define _TIF_FORCED_TF		(1<<TIF_FORCED_TF)
+#define _TIF_FREEZER_HELD	(1<<TIF_FREEZER_HELD)
 
 /* work to do on interrupt/exception return */
 #define _TIF_WORK_MASK \
include/asm-ia64/thread_info.h +4 -0
Index: linux-2.6.21-rc6/include/asm-ia64/thread_info.h
===================================================================
@@ -90,6 +90,9 @@ struct thread_info {
 #define TIF_MCA_INIT		18	/* this task is processing MCA or INIT */
 #define TIF_DB_DISABLED		19	/* debug trap disabled for fsyscall */
 #define TIF_FREEZE		20	/* is freezing for suspend */
+#define TIF_FREEZER_HELD	21	/* is temporarily holding up the
+					 * process freezer
+					 */
 
 #define _TIF_SYSCALL_TRACE	(1 << TIF_SYSCALL_TRACE)
 #define _TIF_SYSCALL_AUDIT	(1 << TIF_SYSCALL_AUDIT)
@@ -102,6 +105,7 @@ struct thread_info {
 #define _TIF_MCA_INIT		(1 << TIF_MCA_INIT)
 #define _TIF_DB_DISABLED	(1 << TIF_DB_DISABLED)
 #define _TIF_FREEZE		(1 << TIF_FREEZE)
+#define _TIF_FREEZER_HELD	(1 << TIF_FREEZER_HELD)
 
 /* "work to do on user-return" bits */
 #define TIF_ALLWORK_MASK	(_TIF_NOTIFY_RESUME|_TIF_SIGPENDING|_TIF_NEED_RESCHED|_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT)
include/asm-mips/thread_info.h +2 -0
Index: linux-2.6.21-rc6/include/asm-mips/thread_info.h
===================================================================
@@ -120,6 +120,7 @@ register struct thread_info *__current_t
 #define TIF_MEMDIE		18
 #define TIF_FREEZE		19
 #define TIF_ALLOW_FP_IN_KERNEL	20
+#define TIF_FREEZER_HELD	21
 #define TIF_SYSCALL_TRACE	31	/* syscall trace active */
 
 #define _TIF_SYSCALL_TRACE	(1<<TIF_SYSCALL_TRACE)
@@ -132,6 +133,7 @@ register struct thread_info *__current_t
 #define _TIF_USEDFPU		(1<<TIF_USEDFPU)
 #define _TIF_POLLING_NRFLAG	(1<<TIF_POLLING_NRFLAG)
 #define _TIF_FREEZE		(1<<TIF_FREEZE)
+#define _TIF_FREEZER_HELD	(1<<TIF_FREEZER_HELD)
 
 /* work to do on interrupt/exception return */
 #define _TIF_WORK_MASK		(0x0000ffef & ~_TIF_SECCOMP)
include/asm-powerpc/thread_info.h +4 -0
Index: linux-2.6.21-rc6/include/asm-powerpc/thread_info.h
===================================================================
@@ -123,6 +123,9 @@ static inline struct thread_info *curren
 #define TIF_NOERROR		14	/* Force successful syscall return */
 #define TIF_RESTORE_SIGMASK	15	/* Restore signal mask in do_signal */
 #define TIF_FREEZE		16	/* Freezing for suspend */
+#define TIF_FREEZER_HELD	17	/* Is temporarily holding up the
+					 * process freezer.
+					 */
 
 /* as above, but as bit values */
 #define _TIF_SYSCALL_TRACE	(1<<TIF_SYSCALL_TRACE)
@@ -140,6 +143,7 @@ static inline struct thread_info *curren
 #define _TIF_NOERROR		(1<<TIF_NOERROR)
 #define _TIF_RESTORE_SIGMASK	(1<<TIF_RESTORE_SIGMASK)
 #define _TIF_FREEZE		(1<<TIF_FREEZE)
+#define _TIF_FREEZER_HELD	(1<<TIF_FREEZER_HELD)
 #define _TIF_SYSCALL_T_OR_A	(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT|_TIF_SECCOMP)
 
 #define _TIF_USER_WORK_MASK	(_TIF_NOTIFY_RESUME | _TIF_SIGPENDING | \
include/asm-sh/thread_info.h +2 -0
Index: linux-2.6.21-rc6/include/asm-sh/thread_info.h
===================================================================
@@ -116,6 +116,7 @@ static inline struct thread_info *curren
 #define TIF_POLLING_NRFLAG	17	/* true if poll_idle() is polling TIF_NEED_RESCHED */
 #define TIF_MEMDIE		18
 #define TIF_FREEZE		19
+#define TIF_FREEZER_HELD	20
 
 #define _TIF_SYSCALL_TRACE	(1<<TIF_SYSCALL_TRACE)
 #define _TIF_NOTIFY_RESUME	(1<<TIF_NOTIFY_RESUME)
@@ -126,6 +127,7 @@ static inline struct thread_info *curren
 #define _TIF_USEDFPU		(1<<TIF_USEDFPU)
 #define _TIF_POLLING_NRFLAG	(1<<TIF_POLLING_NRFLAG)
 #define _TIF_FREEZE		(1<<TIF_FREEZE)
+#define _TIF_FREEZER_HELD	(1<<TIF_FREEZER_HELD)
 
 #define _TIF_WORK_MASK		0x000000FE	/* work to do on interrupt/exception return */
 #define _TIF_ALLWORK_MASK	0x000000FF	/* work to do on any return to u-space */
include/asm-x86_64/thread_info.h +4 -0
Index: linux-2.6.21-rc6/include/asm-x86_64/thread_info.h
===================================================================
@@ -123,6 +123,9 @@ static inline struct thread_info *stack_
 #define TIF_DEBUG		21	/* uses debug registers */
 #define TIF_IO_BITMAP		22	/* uses I/O bitmap */
 #define TIF_FREEZE		23	/* is freezing for suspend */
+#define TIF_FREEZER_HELD	24	/* is temporarily holding up the
+					 * process freezer
+					 */
 
 #define _TIF_SYSCALL_TRACE	(1<<TIF_SYSCALL_TRACE)
 #define _TIF_NOTIFY_RESUME	(1<<TIF_NOTIFY_RESUME)
@@ -140,6 +143,7 @@ static inline struct thread_info *stack_
 #define _TIF_DEBUG		(1<<TIF_DEBUG)
 #define _TIF_IO_BITMAP		(1<<TIF_IO_BITMAP)
 #define _TIF_FREEZE		(1<<TIF_FREEZE)
+#define _TIF_FREEZER_HELD	(1<<TIF_FREEZER_HELD)
 
 /* work to do on interrupt/exception return */
 #define _TIF_WORK_MASK \
Signature · 5 lines
Gautham R Shenoy
Linux Technology Center
IBM India.
"Freedom comes with a price tag of responsibility, which is still a bargain,
because Freedom is priceless!"
maintainer
#15 lore raw

Re: [RFC PATCH(experimental) 2/2] Fix freezer-kthread_stop race

Quoted 8 lines · On Thu, 19 Apr 2007 17:34:19 +0530 Gautham R Shenoy <[email protected]> wrote:

Threads which wait for completion on a frozen thread might result in causing the freezer to fail, if the waiting thread is freezeable.

There are some well known cases where it's preferable to temporarily thaw the frozen process, finish the wait for completion and allow both the processes to call try_to_freeze.

kthread_stop is one such case.

hm.

Quoted 1 line

flush_workqueue might be another.

flush_workqueue() just needs to die. I think there are (almost) no legitimate users of it once cancel_work_sync() is merged.

Quoted 1 line

This patch attempts to address such a situation with a fix for kthread_stop.

Via wholly undescribed means :(

Quoted 1 line

Strictly experimental. Compile tested on i386.

Rather than doing <whatever you did>, perhaps we could make the freezing process a dual-pass thing. On pass 1, mark all the threads as "we'll be freezing you soon" and on the second pass, do the actual freezing. Then, in problematic places such as kthread_stop() we can look to see if we'll soon be asked to freeze and if so, run try_to_freeze().

Of course, running try_to_freeze() in kthread_stop() would be very wrong, so we'd actually need to do it in callers, preferably via a new kthread_stop_freezeable() wrapper.

And the two-pass-freeze thing is of course racy. It's also unnecessary:
setting a flag on every task in the machine is equivalent to setting a global variable. So perhaps just use a global variable?

int kthread_stop_freezeable(struct task_struct *k)

{
	if (freeze_state == ABOUT_TO_START) {
		wait_for(freeze_state == STARTED);
		try_to_freeze();
	}
	kthread_stop(k);
}

which is theoretically racy if another freeze_processes() starts immediately. Anyway - please have a think about it ;)

kernel/power/process.c line 34

Quoted 4 lines
kernel/power/process.c +4 -0
+static struct freezer_status_struct freezer_status = {
+				.lock = SPIN_LOCK_UNLOCKED,
+				.count = 0,
+			};

SPIN_LOCK_UNLOCKED is deprecated (it subverts lockdep)

Quoted 19 lines

static inline int freezeable(struct task_struct * p) {

kernel/power/process.c +3 -1
 	if ((p == current) ||
@@ -45,7 +55,8 @@ void refrigerator(void)
 		 * *after* the freezer did the freezeable() check
 		 * on us.
 		 */
-		if (current->flags & PF_NOFREEZE) {
+		if ((current->flags & PF_NOFREEZE) ||
+		    test_tsk_thread_flag(current, TIF_FREEZER_HELD)) {
 			clear_tsk_thread_flag(current, TIF_FREEZE);
 			task_unlock(current);
 			return;
@@ -63,12 +74,16 @@ void refrigerator(void)
 	recalc_sigpending(); /* We sent fake signal, clean it up */
 	spin_unlock_irq(&current->sighand->siglock);
 
+	task_lock(current);
 	for (;;) {

kernel/power/process.c void refrigerator(void) · line 88

Quoted 10 lines
kernel/power/process.c +3 -0
 		set_current_state(TASK_UNINTERRUPTIBLE);
 		if (!frozen(current))
 			break;
+		task_unlock(current);
 		schedule();
+		task_lock(current);
 	}
+	task_unlock(current);
 	pr_debug("%s left refrigerator\n", current->comm);
 	current->state = save;

I guess we should use set_current_state() here.

kernel/power/process.c static void thaw_tasks(int thaw_user_spa · line 301

Quoted 8 lines
kernel/power/process.c +7 -0
+
+	if (thaw_user_space) {
+		spin_lock(&freezer_status.lock);
+			if (freezer_status.count < 0)
+				freezer_status.count++;
+		spin_unlock(&freezer_status.lock);
+	}
 }

whitespace went wrong

Quoted 27 lines
include/asm-ia64/thread_info.h +4 -0
+#define TIF_FREEZER_HELD	21	/* is temporarily holding up the
+					 * process freezer
+					 */
 
 #define _TIF_SYSCALL_TRACE	(1 << TIF_SYSCALL_TRACE)
 #define _TIF_SYSCALL_AUDIT	(1 << TIF_SYSCALL_AUDIT)
@@ -102,6 +105,7 @@ struct thread_info {
 #define _TIF_MCA_INIT		(1 << TIF_MCA_INIT)
 #define _TIF_DB_DISABLED	(1 << TIF_DB_DISABLED)
 #define _TIF_FREEZE		(1 << TIF_FREEZE)
+#define _TIF_FREEZER_HELD	(1 << TIF_FREEZER_HELD)
 
 /* "work to do on user-return" bits */
 #define TIF_ALLWORK_MASK	(_TIF_NOTIFY_RESUME|_TIF_SIGPENDING|_TIF_NEED_RESCHED|_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT)

Index: linux-2.6.21-rc6/include/asm-mips/thread_info.h
===================================================================

include/asm-mips/thread_info.h +1 -0
@@ -120,6 +120,7 @@ register struct thread_info *__current_t
 #define TIF_MEMDIE		18
 #define TIF_FREEZE		19
 #define TIF_ALLOW_FP_IN_KERNEL	20
+#define TIF_FREEZER_HELD	21
 #define TIF_SYSCALL_TRACE	31	/* syscall trace active */
 
 #define _TIF_SYSCALL_TRACE	(1<<TIF_SYSCALL_TRACE)

include/asm-mips/thread_info.h register struct thread_info *__current_t · line 136

Quoted 4 lines
include/asm-mips/thread_info.h +1 -0
@@ -132,6 +133,7 @@ register struct thread_info *__current_t
 #define _TIF_USEDFPU		(1<<TIF_USEDFPU)
 #define _TIF_POLLING_NRFLAG	(1<<TIF_POLLING_NRFLAG)
 #define _TIF_FREEZE		(1<<TIF_FREEZE)
+#define _TIF_FREEZER_HELD	(1<<TIF_FREEZER_HELD)

hm, all this duplication is unpleasing. We could do something similar to include/linux/buffer_head.h:BH_PrivateStart here: get all architectures to define a TIF_COMMON_STUFF_STARTS_HERE then include asm-generic/whatever.h which defines all the flags which every architecture must define, as TIF_COMMON_STUFF_STARTS_HERE+0, TIF_COMMON_STUFF_STARTS_HERE+1, etc.

#16 lore raw

Re: [RFC PATCH(experimental) 2/2] Fix freezer-kthread_stop race

Quoted 14 lines · Andrew Morton
Quoted 8 lines · On Thu, 19 Apr 2007 17:34:19 +0530 Gautham R Shenoy <[email protected]> wrote:

Threads which wait for completion on a frozen thread might result in causing the freezer to fail, if the waiting thread is freezeable.

There are some well known cases where it's preferable to temporarily thaw the frozen process, finish the wait for completion and allow both the processes to call try_to_freeze.

kthread_stop is one such case.

hm.

Quoted 8 lines
Quoted 1 line

flush_workqueue might be another.

flush_workqueue() just needs to die. I think there are (almost) no legitimate users of it once cancel_work_sync() is merged.

Quoted 1 line

This patch attempts to address such a situation with a fix for kthread_stop.

Via wholly undescribed means :(

Yeah, I have the same problem with it. :-)

Quoted 25 lines
Quoted 1 line

Strictly experimental. Compile tested on i386.

Rather than doing <whatever you did>, perhaps we could make the freezing process a dual-pass thing. On pass 1, mark all the threads as "we'll be freezing you soon" and on the second pass, do the actual freezing. Then, in problematic places such as kthread_stop() we can look to see if we'll soon be asked to freeze and if so, run try_to_freeze().

Of course, running try_to_freeze() in kthread_stop() would be very wrong, so we'd actually need to do it in callers, preferably via a new kthread_stop_freezeable() wrapper.

And the two-pass-freeze thing is of course racy. It's also unnecessary:
setting a flag on every task in the machine is equivalent to setting a global variable. So perhaps just use a global variable?

int kthread_stop_freezeable(struct task_struct *k)

{
	if (freeze_state == ABOUT_TO_START) {
		wait_for(freeze_state == STARTED);
		try_to_freeze();
	}
	kthread_stop(k);
}
Quoted 2 lines

which is theoretically racy if another freeze_processes() starts immediately. Anyway - please have a think about it ;)

Hmm, can't we do something like this instead:

---
 kernel/kthread.c |   10 ++++++++++
 1 file changed, 10 insertions(+)
kernel/kthread.c +10 -0
Index: linux-2.6.21-rc7/kernel/kthread.c
===================================================================
@@ -13,6 +13,7 @@
 #include <linux/file.h>
 #include <linux/module.h>
 #include <linux/mutex.h>
+#include <linux/freezer.h>
 #include <asm/semaphore.h>
 
 /*
@@ -232,6 +233,15 @@ int kthread_stop(struct task_struct *k)
 
 	/* Now set kthread_should_stop() to true, and wake it up. */
 	kthread_stop_info.k = k;
+	if (!(current->flags & PF_NOFREEZE)) {
+		/* If we are freezable, the freezer will wait for us */
+		task_lock(k);
+		k->flags |= PF_NOFREEZE;
+		if (frozen(k))
+			k->flags &= ~PF_FROZEN;
+
+		task_unlock(k);
+	}
 	wake_up_process(k);
 	put_task_struct(k);
 
#17 lore raw

Re: [RFC PATCH(experimental) 2/2] Fix freezer-kthread_stop race

Quoted 22 lines · Rafael J. Wysocki

Hmm, can't we do something like this instead:

---
 kernel/kthread.c |   10 ++++++++++
 1 file changed, 10 insertions(+)

Index: linux-2.6.21-rc7/kernel/kthread.c
===================================================================

kernel/kthread.c +1 -0
@@ -13,6 +13,7 @@
 #include <linux/file.h>
 #include <linux/module.h>
 #include <linux/mutex.h>
+#include <linux/freezer.h>
 #include <asm/semaphore.h>
 
 /*
@@ -232,6 +233,15 @@ int kthread_stop(struct task_struct *k)
 
 	/* Now set kthread_should_stop() to true, and wake it up. */

kernel/kthread.c int kthread_stop(struct task_struct *k) · line 244

Quoted 10 lines
kernel/kthread.c +9 -0
 	kthread_stop_info.k = k;
+	if (!(current->flags & PF_NOFREEZE)) {
+		/* If we are freezable, the freezer will wait for us */
+		task_lock(k);
+		k->flags |= PF_NOFREEZE;
+		if (frozen(k))
+			k->flags &= ~PF_FROZEN;
+
+		task_unlock(k);
+	}

Yes, we can do this for now since the tasks have only two freeze states, namely Freezeable and Non Freezeable.
But with more events like cpu-hotplug and kprobes using the process freezer, the simple check

	if(!(current->flags & PF_NOFREEZE))

may not suffice and something like

	if(!(current->flags & PFE_ALL & global_freeze_mask))

/* global_freeze_mask is the mask of the events for which the system
is freezing. */

appears to be racy. Not that we cannot work it out ;-)

However, I was attempting to solve the generic problem where A waits on B and B is frozen. If A is freezeable (under one of the events) then the freezer will fail. So a solution would be for A to somehow inform B of the dependency and postpone it's freezing.

Since akpm mentioned that flush_workqueue() needs to go, I guess, I am ok with fixing only kthread_stop/kthread_should_stop for the moment. Unless I can spot any other valid case :)

kernel/kthread.c int kthread_stop(struct task_struct *k) · line 247

Quoted 3 lines
kernel/kthread.c +0 -0
 	wake_up_process(k);
 	put_task_struct(k);
 

Thanks and Regards
gautham.

Signature · 5 lines
Gautham R Shenoy
Linux Technology Center
IBM India.
"Freedom comes with a price tag of responsibility, which is still a bargain,
because Freedom is priceless!"
#18 lore raw

Re: [RFC PATCH(experimental) 2/2] Fix freezer-kthread_stop race

Quoted 39 lines · Gautham R Shenoy
Quoted 22 lines · Rafael J. Wysocki

Hmm, can't we do something like this instead:

---
 kernel/kthread.c |   10 ++++++++++
 1 file changed, 10 insertions(+)

Index: linux-2.6.21-rc7/kernel/kthread.c
===================================================================

kernel/kthread.c +1 -0
@@ -13,6 +13,7 @@
 #include <linux/file.h>
 #include <linux/module.h>
 #include <linux/mutex.h>
+#include <linux/freezer.h>
 #include <asm/semaphore.h>
 
 /*
@@ -232,6 +233,15 @@ int kthread_stop(struct task_struct *k)
 
 	/* Now set kthread_should_stop() to true, and wake it up. */

kernel/kthread.c int kthread_stop(struct task_struct *k) · line 244

Quoted 10 lines
kernel/kthread.c +9 -0
 	kthread_stop_info.k = k;
+	if (!(current->flags & PF_NOFREEZE)) {
+		/* If we are freezable, the freezer will wait for us */
+		task_lock(k);
+		k->flags |= PF_NOFREEZE;
+		if (frozen(k))
+			k->flags &= ~PF_FROZEN;
+
+		task_unlock(k);
+	}

Yes, we can do this for now since the tasks have only two freeze states, namely Freezeable and Non Freezeable.
But with more events like cpu-hotplug and kprobes using the process freezer, the simple check

Quoted 8 lines
	if(!(current->flags & PF_NOFREEZE))

may not suffice and something like

	if(!(current->flags & PFE_ALL & global_freeze_mask))

/* global_freeze_mask is the mask of the events for which the system
is freezing. */

appears to be racy. Not that we cannot work it out ;-)

Actually, I thought about it for a while. The thread that is going to stop another one may temporarily mark itself as freezable in all cases, which will have no effect on it, since it's not going to cally try_to_freeze(), but will make the freezer wait for it. Next, after returning from wait_for_completion(), it should restore its old freezability status and that should make the freezer finish.

Quoted 4 lines

However, I was attempting to solve the generic problem where A waits on B and B is frozen. If A is freezeable (under one of the events) then the freezer will fail. So a solution would be for A to somehow inform B of the dependency and postpone it's freezing.

Well, I think it might be simpler to consider each case separately. This way we may be able to avoid introducing the additional TIF_ flag.

Quoted 3 lines

Since akpm mentioned that flush_workqueue() needs to go, I guess, I am ok with fixing only kthread_stop/kthread_should_stop for the moment. Unless I can spot any other valid case :)

Sure. :-)

BTW, if it turns out that we need to introduce yet another freezer-related TIF_ flag, it may be acceptable (?) to move all of the freezer-related flags into a separate member of task_struct (eg. freezer_flags) that can only be manipulated under task_lock().

I mean, we already have four of them (PF_NOFREEZE, PF_FROZEN, PF_FREEZER_SKIP, TIF_FREEZE), and you will need to introduce two more for the freezer-based CPU hotplug, so if yet another one is needed, that will make up almost a separate u8 field ...

Greetings,
Rafael

#19 lore raw

Re: [RFC PATCH(experimental) 2/2] Fix freezer-kthread_stop race

Quoted 7 lines · Rafael J. Wysocki

Actually, I thought about it for a while. The thread that is going to stop another one may temporarily mark itself as freezable in all cases, which will have no effect on it, since it's not going to cally try_to_freeze(), but will make the freezer wait for it. Next, after returning from wait_for_completion(), it should restore its old freezability status and that should make the freezer finish.

But that will have no affect if the thread to be stopped is already frozen. The only affect might be that now, freezer will fail whether the thread that is going to stop another one was freezeable or not.

Concern is whether we can somehow complete these wait_for_completion()'s in the freezing context and reduce the probabilty of freezer failing.

Quoted 9 lines
Quoted 5 lines

However, I was attempting to solve the generic problem where A waits on B and B is frozen. If A is freezeable (under one of the events) then the freezer will fail. So a solution would be for A to somehow inform B of the dependency and postpone it's freezing.

Well, I think it might be simpler to consider each case separately. This way we may be able to avoid introducing the additional TIF_ flag.

Makes sense.

Quoted 11 lines
Quoted 3 lines

Since akpm mentioned that flush_workqueue() needs to go, I guess, I am ok with fixing only kthread_stop/kthread_should_stop for the moment. Unless I can spot any other valid case :)

Sure. :-)

BTW, if it turns out that we need to introduce yet another freezer-related TIF_ flag, it may be acceptable (?) to move all of the freezer-related flags into a separate member of task_struct (eg. freezer_flags) that can only be manipulated under task_lock().

Quoted 4 lines

I mean, we already have four of them (PF_NOFREEZE, PF_FROZEN, PF_FREEZER_SKIP, TIF_FREEZE), and you will need to introduce two more for the freezer-based CPU hotplug, so if yet another one is needed, that will make up almost a separate u8 field ...

I am perfectly ok with it. But I am not sure if everybody would agree to have another field in the task struct, though in this case it does make sense :-)

Quoted 4 lines

Greetings,
Rafael
-

Thanks and Regards
gautham.

Signature · 5 lines
Gautham R Shenoy
Linux Technology Center
IBM India.
"Freedom comes with a price tag of responsibility, which is still a bargain,
because Freedom is priceless!"
#20 lore raw

Re: [RFC PATCH(experimental) 2/2] Fix freezer-kthread_stop race

Quoted 9 lines · Gautham R Shenoy
Quoted 7 lines · Rafael J. Wysocki

Actually, I thought about it for a while. The thread that is going to stop another one may temporarily mark itself as freezable in all cases, which will have no effect on it, since it's not going to cally try_to_freeze(), but will make the freezer wait for it. Next, after returning from wait_for_completion(), it should restore its old freezability status and that should make the freezer finish.

Quoted 6 lines

But that will have no affect if the thread to be stopped is already frozen. The only affect might be that now, freezer will fail whether the thread that is going to stop another one was freezeable or not.

Concern is whether we can somehow complete these wait_for_completion()'s in the freezing context and reduce the probabilty of freezer failing.

To be precise, I was thinking about something like this (in pseudo-code):

	kthread_stop_info.k = k;
	save_freezable_status(current);
	set_always_freezable(current);
	/* Now, we know that the freezer will wait for us, although we're not
	 * really going to freeze
	 */
	task_lock(k);
	k->flags |= PF_NOFREEZE;
	if (frozen(k))
		k->flags &= ~PF_FROZEN;

	task_unlock(k);
	wake_up_process(k);
	put_task_struct(k);

	/* Once it dies, reset stop ptr, gather result and we're done. */
	wait_for_completion(&kthread_stop_info.done);
	restore_freezable_status(current);
Quoted 22 lines
Quoted 8 lines
Quoted 4 lines

However, I was attempting to solve the generic problem where A waits on B and B is frozen. If A is freezeable (under one of the events) then the freezer will fail. So a solution would be for A to somehow inform B of the dependency and postpone it's freezing.

Well, I think it might be simpler to consider each case separately. This way we may be able to avoid introducing the additional TIF_ flag.

Makes sense.

Quoted 11 lines
Quoted 3 lines

Since akpm mentioned that flush_workqueue() needs to go, I guess, I am ok with fixing only kthread_stop/kthread_should_stop for the moment. Unless I can spot any other valid case :)

Sure. :-)

BTW, if it turns out that we need to introduce yet another freezer-related TIF_ flag, it may be acceptable (?) to move all of the freezer-related flags into a separate member of task_struct (eg. freezer_flags) that can only be manipulated under task_lock().

Quoted 7 lines
Quoted 4 lines

I mean, we already have four of them (PF_NOFREEZE, PF_FROZEN, PF_FREEZER_SKIP, TIF_FREEZE), and you will need to introduce two more for the freezer-based CPU hotplug, so if yet another one is needed, that will make up almost a separate u8 field ...

I am perfectly ok with it. But I am not sure if everybody would agree to have another field in the task struct, though in this case it does make sense :-)

Well, I'm not sure either, but I guess the most practical way to learn it is to send a patch. ;-)

Greetings,
Rafael

Signature · 3 lines
If you don't have the time to read,
you don't have the time or the tools to write.
		- Stephen King
maintainer
#21 lore raw

Re: [RFC PATCH(experimental) 2/2] Fix freezer-kthread_stop race

In reply to Gautham R Shenoy
Quoted 7 lines · On Fri, 20 Apr 2007 17:56:09 +0530 Gautham R Shenoy <[email protected]> wrote:
Quoted 4 lines

I mean, we already have four of them (PF_NOFREEZE, PF_FROZEN, PF_FREEZER_SKIP, TIF_FREEZE), and you will need to introduce two more for the freezer-based CPU hotplug, so if yet another one is needed, that will make up almost a separate u8 field ...

I am perfectly ok with it. But I am not sure if everybody would agree to have another field in the task struct, though in this case it does make sense :-)

OK by me. You might want to consider making that fields's locking protocol be set_bit(), clear_bit(), etc rather than task_lock().

#22 lore raw

Re: [RFC PATCH(experimental) 2/2] Fix freezer-kthread_stop race

Quoted 11 lines · Andrew Morton
Quoted 8 lines
Quoted 4 lines

I mean, we already have four of them (PF_NOFREEZE, PF_FROZEN, PF_FREEZER_SKIP, TIF_FREEZE), and you will need to introduce two more for the freezer-based CPU hotplug, so if yet another one is needed, that will make up almost a separate u8 field ...

I am perfectly ok with it. But I am not sure if everybody would agree to have another field in the task struct, though in this case it does make sense :-)

OK by me. You might want to consider making that fields's locking protocol be set_bit(), clear_bit(), etc rather than task_lock().

is OK to me too, the extra field isnt a problem.

Ingo

#23 lore raw

Re: [RFC PATCH(experimental) 2/2] Fix freezer-kthread_stop race

Quoted 8 lines · Ingo Molnar
Quoted 5 lines · Andrew Morton
Quoted 5 lines
Quoted 4 lines

I mean, we already have four of them (PF_NOFREEZE, PF_FROZEN, PF_FREEZER_SKIP, TIF_FREEZE), and you will need to introduce two more for the freezer-based CPU hotplug, so if yet another one is needed, that will make up almost a separate u8 field ...

Quoted 8 lines
Quoted 6 lines
Quoted 3 lines

I am perfectly ok with it. But I am not sure if everybody would agree to have another field in the task struct, though in this case it does make sense :-)

OK by me. You might want to consider making that fields's locking protocol be set_bit(), clear_bit(), etc rather than task_lock().

is OK to me too, the extra field isnt a problem.

OK, so I'll try to prepare a patch introducing it over the weekend. :-)

Greetings,
Rafael

#24 lore raw

cover Separate freezer flags

In reply to Ingo Molnar

Hi,

The following three patches are related to the separation of the freezer flags from process/threadinfo flags.

The first patch separates the freezer from the PM code, because it's no longer a PM-specific piece of code. This also makes the second patch look better.

The second patch introduces the freezer_flags field of task_struct, present only if the freezer is compiled in. All of the freezer-related flags per-task flags are moved to this field and auxiliary functions for operating them are defined in freezer.h . This overlaps with the Gautham's work to some extent, but I think it's better to introduce these changes independently of the CPU hotplug code.

The third patch is a bonus. ;-)

Greetings,
Rafael

#25 lore raw

RFC PATCH 1/3 Separate freezer from PM code

From: Rafael J. Wysocki <[email protected]>

Now that the freezer is used by kprobes, it is no longer a PM-specific piece of code. Move the freezer code out of kernel/power and introduce the CONFIG_FREEZER option that will be chosen automatically if PM or KPROBES is set.

---
 arch/arm/Kconfig         |    5 +
 arch/avr32/Kconfig.debug |    5 +
 arch/blackfin/Kconfig    |    5 +
 arch/frv/Kconfig         |    5 +
 arch/i386/Kconfig        |    5 +
 arch/ia64/Kconfig        |    5 +
 arch/mips/Kconfig        |    5 +
 arch/powerpc/Kconfig     |    5 +
 arch/ppc/Kconfig         |    5 +
 arch/s390/Kconfig        |    5 +
 arch/sh/Kconfig          |    5 +
 arch/sparc64/Kconfig     |    5 +
 arch/x86_64/Kconfig      |    8 +
 include/linux/freezer.h  |    2 
 kernel/Makefile          |    1 
 kernel/freezer.c         |  224 +++++++++++++++++++++++++++++++++++++++++++++++
 kernel/kprobes.c         |    2 
 kernel/power/Makefile    |    2 
 kernel/power/process.c   |  224 -----------------------------------------------
 19 files changed, 296 insertions(+), 227 deletions(-)
arch/x86_64/Kconfig +8 -0
Index: linux-2.6.21-rc6-mm1/arch/x86_64/Kconfig
===================================================================
@@ -695,6 +695,14 @@ config GENERIC_PENDING_IRQ
 	depends on GENERIC_HARDIRQS && SMP
 	default y
 
+#
+# Use the tasks freezer
+#
+config FREEZER
+	bool
+	default y
+	depends on PM || KPROBES
+
 menu "Power management options"
 
 source kernel/power/Kconfig
arch/avr32/Kconfig.debug +5 -0
Index: linux-2.6.21-rc6-mm1/arch/avr32/Kconfig.debug
===================================================================
@@ -17,3 +17,8 @@ config KPROBES
           If in doubt, say "N".
 
 endmenu
+
+config FREEZER
+	bool
+	default y
+	depends on KPROBES
arch/frv/Kconfig +5 -0
Index: linux-2.6.21-rc6-mm1/arch/frv/Kconfig
===================================================================
@@ -364,6 +364,11 @@ source "drivers/pcmcia/Kconfig"
 #	  sleep-deprived psychotic hacker types can say Y now, everyone else
 #	  should probably wait a while.
 
+config FREEZER
+	bool
+	default y
+	depends on PM
+
 menu "Power management options"
 source kernel/power/Kconfig
 endmenu
arch/i386/Kconfig +5 -0
Index: linux-2.6.21-rc6-mm1/arch/i386/Kconfig
===================================================================
@@ -912,6 +912,11 @@ config ARCH_ENABLE_MEMORY_HOTPLUG
 	def_bool y
 	depends on HIGHMEM
 
+config FREEZER
+	bool
+	default y
+	depends on PM || KPROBES
+
 menu "Power management options (ACPI, APM)"
 	depends on !X86_VOYAGER
 
arch/ia64/Kconfig +5 -0
Index: linux-2.6.21-rc6-mm1/arch/ia64/Kconfig
===================================================================
@@ -490,6 +490,11 @@ source "fs/Kconfig.binfmt"
 
 endmenu
 
+config FREEZER
+	bool
+	default y
+	depends on PM || KPROBES
+
 menu "Power management and ACPI"
 
 source "kernel/power/Kconfig"
arch/powerpc/Kconfig +5 -0
Index: linux-2.6.21-rc6-mm1/arch/powerpc/Kconfig
===================================================================
@@ -569,6 +569,11 @@ config CMDLINE
 	  some command-line options at build time by entering them here.  In
 	  most cases you will need to specify the root device here.
 
+config FREEZER
+	bool
+	default y
+	depends on PM || KPROBES
+
 if !44x || BROKEN
 source kernel/power/Kconfig
 endif
arch/ppc/Kconfig +5 -0
Index: linux-2.6.21-rc6-mm1/arch/ppc/Kconfig
===================================================================
@@ -1154,6 +1154,11 @@ config PROC_HARDWARE
 source "drivers/zorro/Kconfig"
 
 if !44x || BROKEN
+config FREEZER
+	bool
+	default y
+	depends on PM
+
 source kernel/power/Kconfig
 endif
 
arch/s390/Kconfig +5 -0
Index: linux-2.6.21-rc6-mm1/arch/s390/Kconfig
===================================================================
@@ -564,6 +564,11 @@ config KPROBES
 	  for kernel debugging, non-intrusive instrumentation and testing.
 	  If in doubt, say "N".
 
+config FREEZER
+	bool
+	default y
+	depends on KPROBES
+
 source "kernel/Kconfig.marker"
 
 source "lib/Kconfig.statistic"
arch/sh/Kconfig +5 -0
Index: linux-2.6.21-rc6-mm1/arch/sh/Kconfig
===================================================================
@@ -688,6 +688,11 @@ source "fs/Kconfig.binfmt"
 
 endmenu
 
+config FREEZER
+	bool
+	default y
+	depends on PM
+
 menu "Power management options (EXPERIMENTAL)"
 depends on EXPERIMENTAL
 
arch/sparc64/Kconfig +5 -0
Index: linux-2.6.21-rc6-mm1/arch/sparc64/Kconfig
===================================================================
@@ -430,6 +430,11 @@ config KPROBES
 	  for kernel debugging, non-intrusive instrumentation and testing.
 	  If in doubt, say "N".
 
+config FREEZER
+	bool
+	default y
+	depends on KPROBES
+
 source "kernel/Kconfig.marker"
 
 endmenu
kernel/freezer.c new file +224 -0

224 lines not on this page · raw message

kernel/power/process.c deleted +0 -224

224 lines not on this page · raw message

kernel/Makefile +1 -0
Index: linux-2.6.21-rc6-mm1/kernel/Makefile
===================================================================
@@ -33,6 +33,7 @@ obj-$(CONFIG_MODULES) += module.o
 obj-$(CONFIG_KALLSYMS) += kallsyms.o
 obj-$(CONFIG_STACK_UNWIND) += unwind.o
 obj-$(CONFIG_PM) += power/
+obj-$(CONFIG_FREEZER) += freezer.o
 obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o
 obj-$(CONFIG_KEXEC) += kexec.o
 obj-$(CONFIG_COMPAT) += compat.o
kernel/power/Makefile +1 -1
Index: linux-2.6.21-rc6-mm1/kernel/power/Makefile
===================================================================
@@ -3,7 +3,7 @@ ifeq ($(CONFIG_PM_DEBUG),y)
 EXTRA_CFLAGS	+=	-DDEBUG
 endif
 
-obj-y				:= main.o process.o console.o
+obj-y				:= main.o console.o
 obj-$(CONFIG_PM_LEGACY)		+= pm.o
 obj-$(CONFIG_SOFTWARE_SUSPEND)	+= swsusp.o disk.o snapshot.o swap.o user.o
 
kernel/kprobes.c +1 -1
Index: linux-2.6.21-rc6-mm1/kernel/kprobes.c
===================================================================
@@ -107,7 +107,7 @@ static int collect_garbage_slots(void);
 static int __kprobes check_safety(void)
 {
 	int ret = 0;
-#if defined(CONFIG_PREEMPT) && defined(CONFIG_PM)
+#ifdef CONFIG_PREEMPT
 	ret = freeze_processes();
 	if (ret == 0) {
 		struct task_struct *p, *q;
include/linux/freezer.h +1 -1
Index: linux-2.6.21-rc6-mm1/include/linux/freezer.h
===================================================================
@@ -2,7 +2,7 @@
 
 #include <linux/sched.h>
 
-#ifdef CONFIG_PM
+#ifdef CONFIG_FREEZER
 /*
  * Check if a process has been frozen
  */
arch/arm/Kconfig +5 -0
Index: linux-2.6.21-rc6-mm1/arch/arm/Kconfig
===================================================================
@@ -916,6 +916,11 @@ menu "Power management options"
 
 source "kernel/power/Kconfig"
 
+config FREEZER
+	bool
+	default y
+	depends on PM
+
 endmenu
 
 source "net/Kconfig"
arch/blackfin/Kconfig +5 -0
Index: linux-2.6.21-rc6-mm1/arch/blackfin/Kconfig
===================================================================
@@ -818,6 +818,11 @@ endmenu
 menu "Power management options"
 source "kernel/power/Kconfig"
 
+config FREEZER
+	bool
+	default y
+	depends on PM
+
 choice
 	prompt "Select PM Wakeup Event Source"
 	default PM_WAKEUP_GPIO_BY_SIC_IWR
arch/mips/Kconfig +5 -0
Index: linux-2.6.21-rc6-mm1/arch/mips/Kconfig
===================================================================
@@ -2138,6 +2138,11 @@ config BINFMT_ELF32
 
 source "kernel/power/Kconfig"
 
+config FREEZER
+	bool
+	default y
+	depends on PM
+
 endmenu
 
 source "net/Kconfig"
#26 lore raw

Re: [RFC][PATCH -mm 1/3] Separate freezer from PM code

Hi!

Quoted 5 lines

Now that the freezer is used by kprobes, it is no longer a PM-specific piece of code. Move the freezer code out of kernel/power and introduce the CONFIG_FREEZER option that will be chosen automatically if PM or KPROBES is set.

ACK.
Pavel

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

RFC PATCH 2/3 freezer: Introduce freezer_flags

In reply to Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

Move all of the freezer-related flags to a separate field in task_struct and introduce functions to operate them using set_bit() etc.

---
 Documentation/power/kernel_threads.txt |    2 -
 Documentation/power/swsusp.txt         |    6 +--
 arch/i386/kernel/apm.c                 |    2 -
 drivers/block/loop.c                   |    2 -
 drivers/char/apm-emulation.c           |    6 +--
 drivers/ieee1394/ieee1394_core.c       |    2 -
 drivers/md/md.c                        |    2 -
 drivers/mmc/card/queue.c               |    3 +
 drivers/mtd/mtd_blkdevs.c              |    3 +
 drivers/scsi/libsas/sas_scsi_host.c    |    2 -
 drivers/scsi/scsi_error.c              |    2 -
 drivers/usb/storage/usb.c              |    2 -
 include/asm-arm/thread_info.h          |    2 -
 include/asm-blackfin/thread_info.h     |    2 -
 include/asm-frv/thread_info.h          |    2 -
 include/asm-i386/thread_info.h         |    2 -
 include/asm-ia64/thread_info.h         |    2 -
 include/asm-mips/thread_info.h         |    2 -
 include/asm-powerpc/thread_info.h      |    2 -
 include/asm-sh/thread_info.h           |    2 -
 include/asm-x86_64/thread_info.h       |    2 -
 include/linux/freezer.h                |   65 ++++++++++++++++++++++++++-------
 include/linux/sched.h                  |    8 ++--
 kernel/fork.c                          |    2 -
 kernel/freezer.c                       |    2 -
 kernel/rcutorture.c                    |    4 +-
 kernel/sched.c                         |    2 -
 kernel/softirq.c                       |    2 -
 kernel/softlockup.c                    |    2 -
 kernel/workqueue.c                     |    2 -
 30 files changed, 83 insertions(+), 58 deletions(-)
include/linux/sched.h +4 -4
Index: linux-2.6.21-rc6-mm1/include/linux/sched.h
===================================================================
@@ -1002,7 +1002,10 @@ struct task_struct {
 	/* Deadlock detection and priority inheritance handling */
 	struct rt_mutex_waiter *pi_blocked_on;
 #endif
-
+#ifdef CONFIG_FREEZER
+	/* Used by the process freezer, defined in freezer.h */
+	unsigned int freezer_flags;
+#endif
 #ifdef CONFIG_DEBUG_MUTEXES
 	/* mutex deadlock detection */
 	struct mutex_waiter *blocked_on;
@@ -1187,8 +1190,6 @@ static inline void put_task_struct(struc
 #define PF_MEMALLOC	0x00000800	/* Allocating memory */
 #define PF_FLUSHER	0x00001000	/* responsible for disk writeback */
 #define PF_USED_MATH	0x00002000	/* if unset the fpu must be initialized before use */
-#define PF_NOFREEZE	0x00008000	/* this thread should not be frozen */
-#define PF_FROZEN	0x00010000	/* frozen for system suspend */
 #define PF_FSTRANS	0x00020000	/* inside a filesystem transaction */
 #define PF_KSWAPD	0x00040000	/* I am kswapd */
 #define PF_SWAPOFF	0x00080000	/* I am in swapoff */
@@ -1200,7 +1201,6 @@ static inline void put_task_struct(struc
 #define PF_SPREAD_SLAB	0x02000000	/* Spread some slab caches over cpuset */
 #define PF_MEMPOLICY	0x10000000	/* Non-default NUMA mempolicy */
 #define PF_MUTEX_TESTER	0x20000000	/* Thread belongs to the rt mutex tester */
-#define PF_FREEZER_SKIP	0x40000000	/* Freezer should not count it as freezeable */
 
 /*
  * Only the _current_ task can read/write to tsk->flags, but other
include/asm-arm/thread_info.h +0 -2
Index: linux-2.6.21-rc6-mm1/include/asm-arm/thread_info.h
===================================================================
@@ -147,7 +147,6 @@ extern void iwmmxt_task_switch(struct th
 #define TIF_POLLING_NRFLAG	16
 #define TIF_USING_IWMMXT	17
 #define TIF_MEMDIE		18
-#define TIF_FREEZE		19
 
 #define _TIF_NOTIFY_RESUME	(1 << TIF_NOTIFY_RESUME)
 #define _TIF_SIGPENDING		(1 << TIF_SIGPENDING)
@@ -155,7 +154,6 @@ extern void iwmmxt_task_switch(struct th
 #define _TIF_SYSCALL_TRACE	(1 << TIF_SYSCALL_TRACE)
 #define _TIF_POLLING_NRFLAG	(1 << TIF_POLLING_NRFLAG)
 #define _TIF_USING_IWMMXT	(1 << TIF_USING_IWMMXT)
-#define _TIF_FREEZE		(1 << TIF_FREEZE)
 
 /*
  * Change these and you break ASM code in entry-common.S
include/asm-blackfin/thread_info.h +0 -2
Index: linux-2.6.21-rc6-mm1/include/asm-blackfin/thread_info.h
===================================================================
@@ -125,7 +125,6 @@ static inline struct thread_info *curren
 					   TIF_NEED_RESCHED */
 #define TIF_MEMDIE              5
 #define TIF_RESTORE_SIGMASK	6	/* restore signal mask in do_signal() */
-#define TIF_FREEZE              7       /* is freezing for suspend */
 #define TIF_SINGLESTEP		8       /* restore singlestep on return to user mode */
 
 /* as above, but as bit values */
@@ -135,7 +134,6 @@ static inline struct thread_info *curren
 #define _TIF_NEED_RESCHED	(1<<TIF_NEED_RESCHED)
 #define _TIF_POLLING_NRFLAG	(1<<TIF_POLLING_NRFLAG)
 #define _TIF_RESTORE_SIGMASK	(1<<TIF_RESTORE_SIGMASK)
-#define _TIF_FREEZE             (1<<TIF_FREEZE)
 #define _TIF_SINGLESTEP		(1<<TIF_SINGLESTEP)
 
 #define _TIF_WORK_MASK		0x0000FFFE	/* work to do on interrupt/exception return */
include/asm-frv/thread_info.h +0 -2
Index: linux-2.6.21-rc6-mm1/include/asm-frv/thread_info.h
===================================================================
@@ -116,7 +116,6 @@ register struct thread_info *__current_t
 #define TIF_RESTORE_SIGMASK	6	/* restore signal mask in do_signal() */
 #define TIF_POLLING_NRFLAG	16	/* true if poll_idle() is polling TIF_NEED_RESCHED */
 #define TIF_MEMDIE		17	/* OOM killer killed process */
-#define TIF_FREEZE		18	/* freezing for suspend */
 
 #define _TIF_SYSCALL_TRACE	(1 << TIF_SYSCALL_TRACE)
 #define _TIF_NOTIFY_RESUME	(1 << TIF_NOTIFY_RESUME)
@@ -126,7 +125,6 @@ register struct thread_info *__current_t
 #define _TIF_IRET		(1 << TIF_IRET)
 #define _TIF_RESTORE_SIGMASK	(1 << TIF_RESTORE_SIGMASK)
 #define _TIF_POLLING_NRFLAG	(1 << TIF_POLLING_NRFLAG)
-#define _TIF_FREEZE		(1 << TIF_FREEZE)
 
 #define _TIF_WORK_MASK		0x0000FFFE	/* work to do on interrupt/exception return */
 #define _TIF_ALLWORK_MASK	0x0000FFFF	/* work to do on any return to u-space */
include/asm-i386/thread_info.h +0 -2
Index: linux-2.6.21-rc6-mm1/include/asm-i386/thread_info.h
===================================================================
@@ -135,7 +135,6 @@ static inline struct thread_info *curren
 #define TIF_MEMDIE		16
 #define TIF_DEBUG		17	/* uses debug registers */
 #define TIF_IO_BITMAP		18	/* uses I/O bitmap */
-#define TIF_FREEZE		19	/* is freezing for suspend */
 #define TIF_FORCED_TF		20	/* true if TF in eflags artificially */
 
 #define _TIF_SYSCALL_TRACE	(1<<TIF_SYSCALL_TRACE)
@@ -149,7 +148,6 @@ static inline struct thread_info *curren
 #define _TIF_RESTORE_SIGMASK	(1<<TIF_RESTORE_SIGMASK)
 #define _TIF_DEBUG		(1<<TIF_DEBUG)
 #define _TIF_IO_BITMAP		(1<<TIF_IO_BITMAP)
-#define _TIF_FREEZE		(1<<TIF_FREEZE)
 #define _TIF_FORCED_TF		(1<<TIF_FORCED_TF)
 
 /* work to do on interrupt/exception return */
include/asm-ia64/thread_info.h +0 -2
Index: linux-2.6.21-rc6-mm1/include/asm-ia64/thread_info.h
===================================================================
@@ -89,7 +89,6 @@ struct thread_info {
 #define TIF_MEMDIE		17
 #define TIF_MCA_INIT		18	/* this task is processing MCA or INIT */
 #define TIF_DB_DISABLED		19	/* debug trap disabled for fsyscall */
-#define TIF_FREEZE		20	/* is freezing for suspend */
 
 #define _TIF_SYSCALL_TRACE	(1 << TIF_SYSCALL_TRACE)
 #define _TIF_SYSCALL_AUDIT	(1 << TIF_SYSCALL_AUDIT)
@@ -101,7 +100,6 @@ struct thread_info {
 #define _TIF_POLLING_NRFLAG	(1 << TIF_POLLING_NRFLAG)
 #define _TIF_MCA_INIT		(1 << TIF_MCA_INIT)
 #define _TIF_DB_DISABLED	(1 << TIF_DB_DISABLED)
-#define _TIF_FREEZE		(1 << TIF_FREEZE)
 
 /* "work to do on user-return" bits */
 #define TIF_ALLWORK_MASK	(_TIF_NOTIFY_RESUME|_TIF_SIGPENDING|_TIF_NEED_RESCHED|_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT)
include/asm-mips/thread_info.h +0 -2
Index: linux-2.6.21-rc6-mm1/include/asm-mips/thread_info.h
===================================================================
@@ -118,7 +118,6 @@ register struct thread_info *__current_t
 #define TIF_USEDFPU		16	/* FPU was used by this task this quantum (SMP) */
 #define TIF_POLLING_NRFLAG	17	/* true if poll_idle() is polling TIF_NEED_RESCHED */
 #define TIF_MEMDIE		18
-#define TIF_FREEZE		19
 #define TIF_ALLOW_FP_IN_KERNEL	20
 #define TIF_SYSCALL_TRACE	31	/* syscall trace active */
 
@@ -131,7 +130,6 @@ register struct thread_info *__current_t
 #define _TIF_RESTORE_SIGMASK	(1<<TIF_RESTORE_SIGMASK)
 #define _TIF_USEDFPU		(1<<TIF_USEDFPU)
 #define _TIF_POLLING_NRFLAG	(1<<TIF_POLLING_NRFLAG)
-#define _TIF_FREEZE		(1<<TIF_FREEZE)
 
 /* work to do on interrupt/exception return */
 #define _TIF_WORK_MASK		(0x0000ffef & ~_TIF_SECCOMP)
include/asm-powerpc/thread_info.h +0 -2
Index: linux-2.6.21-rc6-mm1/include/asm-powerpc/thread_info.h
===================================================================
@@ -122,7 +122,6 @@ static inline struct thread_info *curren
 #define TIF_RESTOREALL		12	/* Restore all regs (implies NOERROR) */
 #define TIF_NOERROR		14	/* Force successful syscall return */
 #define TIF_RESTORE_SIGMASK	15	/* Restore signal mask in do_signal */
-#define TIF_FREEZE		16	/* Freezing for suspend */
 
 /* as above, but as bit values */
 #define _TIF_SYSCALL_TRACE	(1<<TIF_SYSCALL_TRACE)
@@ -139,7 +138,6 @@ static inline struct thread_info *curren
 #define _TIF_RESTOREALL		(1<<TIF_RESTOREALL)
 #define _TIF_NOERROR		(1<<TIF_NOERROR)
 #define _TIF_RESTORE_SIGMASK	(1<<TIF_RESTORE_SIGMASK)
-#define _TIF_FREEZE		(1<<TIF_FREEZE)
 #define _TIF_SYSCALL_T_OR_A	(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT|_TIF_SECCOMP)
 
 #define _TIF_USER_WORK_MASK	(_TIF_NOTIFY_RESUME | _TIF_SIGPENDING | \
include/asm-sh/thread_info.h +0 -2
Index: linux-2.6.21-rc6-mm1/include/asm-sh/thread_info.h
===================================================================
@@ -115,7 +115,6 @@ static inline struct thread_info *curren
 #define TIF_USEDFPU		16	/* FPU was used by this task this quantum (SMP) */
 #define TIF_POLLING_NRFLAG	17	/* true if poll_idle() is polling TIF_NEED_RESCHED */
 #define TIF_MEMDIE		18
-#define TIF_FREEZE		19
 
 #define _TIF_SYSCALL_TRACE	(1<<TIF_SYSCALL_TRACE)
 #define _TIF_NOTIFY_RESUME	(1<<TIF_NOTIFY_RESUME)
@@ -125,7 +124,6 @@ static inline struct thread_info *curren
 #define _TIF_SINGLESTEP		(1<<TIF_SINGLESTEP)
 #define _TIF_USEDFPU		(1<<TIF_USEDFPU)
 #define _TIF_POLLING_NRFLAG	(1<<TIF_POLLING_NRFLAG)
-#define _TIF_FREEZE		(1<<TIF_FREEZE)
 
 #define _TIF_WORK_MASK		0x000000FE	/* work to do on interrupt/exception return */
 #define _TIF_ALLWORK_MASK	0x000000FF	/* work to do on any return to u-space */
include/asm-x86_64/thread_info.h +0 -2
Index: linux-2.6.21-rc6-mm1/include/asm-x86_64/thread_info.h
===================================================================
@@ -122,7 +122,6 @@ static inline struct thread_info *stack_
 #define TIF_MEMDIE		20
 #define TIF_DEBUG		21	/* uses debug registers */
 #define TIF_IO_BITMAP		22	/* uses I/O bitmap */
-#define TIF_FREEZE		23	/* is freezing for suspend */
 
 #define _TIF_SYSCALL_TRACE	(1<<TIF_SYSCALL_TRACE)
 #define _TIF_NOTIFY_RESUME	(1<<TIF_NOTIFY_RESUME)
@@ -139,7 +138,6 @@ static inline struct thread_info *stack_
 #define _TIF_ABI_PENDING	(1<<TIF_ABI_PENDING)
 #define _TIF_DEBUG		(1<<TIF_DEBUG)
 #define _TIF_IO_BITMAP		(1<<TIF_IO_BITMAP)
-#define _TIF_FREEZE		(1<<TIF_FREEZE)
 
 /* work to do on interrupt/exception return */
 #define _TIF_WORK_MASK \
include/linux/freezer.h +53 -12

135 lines not on this page · raw message

kernel/sched.c +1 -1
Index: linux-2.6.21-rc6-mm1/kernel/sched.c
===================================================================
@@ -5431,7 +5431,7 @@ migration_call(struct notifier_block *nf
 		p = kthread_create(migration_thread, hcpu, "migration/%d",cpu);
 		if (IS_ERR(p))
 			return NOTIFY_BAD;
-		p->flags |= PF_NOFREEZE;
+		freezer_exempt(p);
 		kthread_bind(p, cpu);
 		/* Must be high prio: stop_machine expects to yield to it. */
 		rq = task_rq_lock(p, &flags);
arch/i386/kernel/apm.c +1 -1
Index: linux-2.6.21-rc6-mm1/arch/i386/kernel/apm.c
===================================================================
@@ -2313,7 +2313,7 @@ static int __init apm_init(void)
 		remove_proc_entry("apm", NULL);
 		return err;
 	}
-	kapmd_task->flags |= PF_NOFREEZE;
+	freezer_exempt(kapmd_task);
 	wake_up_process(kapmd_task);
 
 	if (num_online_cpus() > 1 && !smp ) {
drivers/block/loop.c +1 -1
Index: linux-2.6.21-rc6-mm1/drivers/block/loop.c
===================================================================
@@ -586,7 +586,7 @@ static int loop_thread(void *data)
 	 * hence, it mustn't be stopped at all
 	 * because it could be indirectly used during suspension
 	 */
-	current->flags |= PF_NOFREEZE;
+	freezer_exempt(current);
 
 	set_user_nice(current, -20);
 
drivers/char/apm-emulation.c +3 -3
Index: linux-2.6.21-rc6-mm1/drivers/char/apm-emulation.c
===================================================================
@@ -336,7 +336,7 @@ apm_ioctl(struct inode * inode, struct f
 			 * threads.
 			 */
 			flags = current->flags;
-			current->flags |= PF_NOFREEZE;
+			freezer_exempt(current);
 
 			wait_event(apm_suspend_waitqueue,
 				   as->suspend_state == SUSPEND_DONE);
@@ -372,7 +372,7 @@ apm_ioctl(struct inode * inode, struct f
 			 * threads.
 			 */
 			flags = current->flags;
-			current->flags |= PF_NOFREEZE;
+			freezer_exempt(current);
 
 			wait_event_interruptible(apm_suspend_waitqueue,
 					 as->suspend_state == SUSPEND_DONE);
@@ -601,7 +601,7 @@ static int __init apm_init(void)
 		kapmd_tsk = NULL;
 		return ret;
 	}
-	kapmd_tsk->flags |= PF_NOFREEZE;
+	freezer_exempt(kapmd_tsk);
 	wake_up_process(kapmd_tsk);
 
 #ifdef CONFIG_PROC_FS
drivers/ieee1394/ieee1394_core.c +1 -1
Index: linux-2.6.21-rc6-mm1/drivers/ieee1394/ieee1394_core.c
===================================================================
@@ -1134,7 +1134,7 @@ static int hpsbpkt_thread(void *__hi)
 	struct list_head tmp;
 	int may_schedule;
 
-	current->flags |= PF_NOFREEZE;
+	freezer_exempt(current);
 
 	while (!kthread_should_stop()) {
 
drivers/md/md.c +1 -1
Index: linux-2.6.21-rc6-mm1/drivers/md/md.c
===================================================================
@@ -4541,7 +4541,7 @@ static int md_thread(void * arg)
 	 * many dirty RAID5 blocks.
 	 */
 
-	current->flags |= PF_NOFREEZE;
+	freezer_exempt(current);
 	allow_signal(SIGKILL);
 	while (!kthread_should_stop()) {
 
drivers/mmc/card/queue.c +2 -1
Index: linux-2.6.21-rc6-mm1/drivers/mmc/card/queue.c
===================================================================
@@ -66,7 +66,8 @@ static int mmc_queue_thread(void *d)
 	 * Set iothread to ensure that we aren't put to sleep by
 	 * the process freezing.  We handle suspension ourselves.
 	 */
-	current->flags |= PF_MEMALLOC|PF_NOFREEZE;
+	current->flags |= PF_MEMALLOC;
+	freezer_exempt(current);
 
 	down(&mq->thread_sem);
 	do {
drivers/mtd/mtd_blkdevs.c +2 -1
Index: linux-2.6.21-rc6-mm1/drivers/mtd/mtd_blkdevs.c
===================================================================
@@ -82,7 +82,8 @@ static int mtd_blktrans_thread(void *arg
 	struct request_queue *rq = tr->blkcore_priv->rq;
 
 	/* we might get involved when memory gets low, so use PF_MEMALLOC */
-	current->flags |= PF_MEMALLOC | PF_NOFREEZE;
+	current->flags |= PF_MEMALLOC;
+	freezer_exempt(current);
 
 	daemonize("%sd", tr->name);
 
drivers/scsi/libsas/sas_scsi_host.c +1 -1
Index: linux-2.6.21-rc6-mm1/drivers/scsi/libsas/sas_scsi_host.c
===================================================================
@@ -871,7 +871,7 @@ static int sas_queue_thread(void *_sas_h
 	struct scsi_core *core = &sas_ha->core;
 
 	daemonize("sas_queue_%d", core->shost->host_no);
-	current->flags |= PF_NOFREEZE;
+	freezer_exempt(current);
 
 	complete(&queue_th_comp);
 
drivers/scsi/scsi_error.c +1 -1
Index: linux-2.6.21-rc6-mm1/drivers/scsi/scsi_error.c
===================================================================
@@ -1536,7 +1536,7 @@ int scsi_error_handler(void *data)
 {
 	struct Scsi_Host *shost = data;
 
-	current->flags |= PF_NOFREEZE;
+	freezer_exempt(current);
 
 	/*
 	 * We use TASK_INTERRUPTIBLE so that the thread is not
drivers/usb/storage/usb.c +1 -1
Index: linux-2.6.21-rc6-mm1/drivers/usb/storage/usb.c
===================================================================
@@ -301,7 +301,7 @@ static int usb_stor_control_thread(void 
 	struct us_data *us = (struct us_data *)__us;
 	struct Scsi_Host *host = us_to_host(us);
 
-	current->flags |= PF_NOFREEZE;
+	freezer_exempt(current);
 
 	for(;;) {
 		try_to_freeze();
kernel/fork.c +1 -1
Index: linux-2.6.21-rc6-mm1/kernel/fork.c
===================================================================
@@ -922,7 +922,7 @@ static inline void copy_flags(unsigned l
 {
 	unsigned long new_flags = p->flags;
 
-	new_flags &= ~(PF_SUPERPRIV | PF_NOFREEZE);
+	new_flags &= ~PF_SUPERPRIV;
 	new_flags |= PF_FORKNOEXEC;
 	new_flags |= PF_STARTING;
 	p->flags = new_flags;
kernel/rcutorture.c +2 -2
Index: linux-2.6.21-rc6-mm1/kernel/rcutorture.c
===================================================================
@@ -559,7 +559,7 @@ rcu_torture_fakewriter(void *arg)
 
 	VERBOSE_PRINTK_STRING("rcu_torture_fakewriter task started");
 	set_user_nice(current, 19);
-	current->flags |= PF_NOFREEZE;
+	freezer_exempt(current);
 
 	do {
 		schedule_timeout_uninterruptible(1 + rcu_random(&rand)%10);
@@ -590,7 +590,7 @@ rcu_torture_reader(void *arg)
 
 	VERBOSE_PRINTK_STRING("rcu_torture_reader task started");
 	set_user_nice(current, 19);
-	current->flags |= PF_NOFREEZE;
+	freezer_exempt(current);
 
 	do {
 		idx = cur_ops->readlock();
kernel/softirq.c +1 -1
Index: linux-2.6.21-rc6-mm1/kernel/softirq.c
===================================================================
@@ -490,7 +490,7 @@ void __init softirq_init(void)
 static int ksoftirqd(void * __bind_cpu)
 {
 	set_user_nice(current, 19);
-	current->flags |= PF_NOFREEZE;
+	freezer_exempt(current);
 
 	set_current_state(TASK_INTERRUPTIBLE);
 
kernel/softlockup.c +1 -1
Index: linux-2.6.21-rc6-mm1/kernel/softlockup.c
===================================================================
@@ -117,7 +117,7 @@ static int watchdog(void * __bind_cpu)
 	struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 };
 
 	sched_setscheduler(current, SCHED_FIFO, &param);
-	current->flags |= PF_NOFREEZE;
+	freezer_exempt(current);
 
 	/* initialize timestamp */
 	touch_softlockup_watchdog();
kernel/workqueue.c +1 -1
Index: linux-2.6.21-rc6-mm1/kernel/workqueue.c
===================================================================
@@ -292,7 +292,7 @@ static int worker_thread(void *__cwq)
 	struct k_sigaction sa;
 
 	if (!cwq->wq->freezeable)
-		current->flags |= PF_NOFREEZE;
+		freezer_exempt(current);
 
 	/*
 	 * We inherited MPOL_INTERLEAVE from the booting kernel.
kernel/freezer.c +1 -1
Index: linux-2.6.21-rc6-mm1/kernel/freezer.c
===================================================================
@@ -26,7 +26,7 @@
 static inline int freezeable(struct task_struct * p)
 {
 	if ((p == current) ||
-	    (p->flags & PF_NOFREEZE) ||
+	    freezer_should_exempt(p) ||
 	    (p->exit_state != 0))
 		return 0;
 	return 1;
Documentation/power/kernel_threads.txt +1 -1
Index: linux-2.6.21-rc6-mm1/Documentation/power/kernel_threads.txt
===================================================================
@@ -32,7 +32,7 @@ like this:
 	 */
 	daemonize("usb-storage");
 
-	current->flags |= PF_NOFREEZE;
+	freezer_exempt(current);
 
 from drivers/usb/storage/usb.c::usb_stor_control_thread()
 
Documentation/power/swsusp.txt +2 -2
Index: linux-2.6.21-rc6-mm1/Documentation/power/swsusp.txt
===================================================================
@@ -152,8 +152,8 @@ add:
        try_to_freeze();
 
 If the thread is needed for writing the image to storage, you should
-instead set the PF_NOFREEZE process flag when creating the thread (and
-be very careful).
+instead use freezer_exempt() to mark the thread as nonfreezable when creating
+it (and be very careful).
 
 
 Q: What is the difference between "platform" and "shutdown"?
#28 lore raw

Re: [RFC][PATCH -mm 2/3] freezer: Introduce freezer_flags

Quoted 2 lines · Rafael

Move all of the freezer-related flags to a separate field in task_struct and introduce functions to operate them using set_bit() etc.

It's getting time I learned what this freezer thing is.

What would you suggest I read?

I looked in include/linux/freezer.h and didn't see any explanations. I found one Documenation file, power/kernel_threads.txt, that explained the interaction of freezing and kernel threads. I looked in the comments for various 2.6.21-rc6-mm1 freezer* patches, and saw various interesting details.

But I couldn't find any documentation telling me what a freezer was, or what a refrigerator is.

Did I miss something?

Signature · 3 lines
                  I won't rest till it's the best ...
                  Programmer, Linux Scalability
                  Paul Jackson <[email protected]> 1.925.600.0401
#29 lore raw

Re: [RFC][PATCH -mm 2/3] freezer: Introduce freezer_flags

Quoted 14 lines · Paul Jackson
Quoted 2 lines · Rafael

Move all of the freezer-related flags to a separate field in task_struct and introduce functions to operate them using set_bit() etc.

It's getting time I learned what this freezer thing is.

What would you suggest I read?

I looked in include/linux/freezer.h and didn't see any explanations. I found one Documenation file, power/kernel_threads.txt, that explained the interaction of freezing and kernel threads. I looked in the comments for various 2.6.21-rc6-mm1 freezer* patches, and saw various interesting details.

Quoted 4 lines

But I couldn't find any documentation telling me what a freezer was, or what a refrigerator is.

Did I miss something?

Well, unfortunately not. The freezer is not separately documented, mainly because (1) for a long time it's been a part of the suspend code and no one else used it, and (2) because it's been changing a lot recently and it's been a 'moving target' from the documentation point of view.

I'll try to explain how it works.

In short, we use the freezer to make tasks enter a specific function, called the refrigerator, and stay there until they are let out. This is done by calling freeze_processes() (in 2.6.21-rc7 it is located in kernel/power/process.c) that executes try_to_freeze_tasks() separately for userland processes and kernel threads (the sync() in between is needed by the suspend code). try_to_freeze_tasks() sets the FREEZE flag (TIF_FREEZE in 2.6.21-rc7) for each freezable (I'll explain what that means in a while) task and sends a fake signal to it.

Userland processes then go to get_signal_to_deliver(), where they execute try_to_freeze() (defined in include/linux/freezer.h) and call refrigerator() (in kernel/power/process.c), since the FREEZE flag is set. In refrigerator() they reset the FREEZE flag and set the FROZEN flag (in 2.6.21-rc7 it is a process flag defined in sched.h). Next, they loop in refrigerator() until someone resets the FROZEN flag for them. Then we say that they are 'frozen'.

Kernel threads don't call get_signal_to_deliver(), so they have to execute try_to_freeze() directly to go to the refrigerator. Moreover, kernel threads may not want to go there at all, in which case they should set the NOFREEZE flag (in 2.6.21-rc7 it is a process flag defined in sched.h), that makes try_to_freeze_tasks() ignore them.

Apart from the kernel threads that have the NOFREEZE flag set, try_to_freeze_tasks() ignores the task that's running it (its current task) and the tasks that have exit_state different from 0. They all are regarded as 'not freezable'.

Of course, kernel threads that declare themselves as not freezable, by setting the NOFREEZE flag, should be careful enough not to interfere with the subsystem that is using the freezer (ie. has called freeze_processes()), which may be quite difficult in practice, so only a few kernel threads do it (and even some of them really shouldn't).

The subsystem that has called freeze_processes() is responsible for the 'thawing' of tasks, which is done by calling thaw_processes() (defined in kernel/power/process.c). It runs thaw_tasks() (in the same file) for kernel threads and userland processes. This function loops over all tasks and resets the FROZEN flag for them, so that they can leave the refrigerator.

The suspend code uses the freezer to make sure that processes won't get in the way when some suspend-related low-level operations are carried out (like suspending devices, the creation of a suspend image by swsusp etc.). Other subsystems may use it for similar purposes.

Greetings,
Rafael

#30 lore raw

Re: [RFC][PATCH -mm 2/3] freezer: Introduce freezer_flags

Quoted 1 line · Rafael

I'll try to explain how it works.

Ok - thanks. Good explanation of how it works.

One more question - why would I want to do this?

Is this like something that would be useful on a laptop, to suspend activity and reduce battery drain, while preserving the current state of ones sessions and avoiding having to logout or shutdown?

Or are there other good uses for this?

Is it useful for quietting a system down before doing hot plug or unplug of key components, such as processors and memory?

Thanks.

Signature · 3 lines
                  I won't rest till it's the best ...
                  Programmer, Linux Scalability
                  Paul Jackson <[email protected]> 1.925.600.0401
#31 lore raw

Re: [RFC][PATCH -mm 2/3] freezer: Introduce freezer_flags

Quoted 1 line · Paul Jackson

One more question - why would I want to do this?

Check out the FAQ in Documentation/power/swsusp.txt.

Quoted 3 lines

Is this like something that would be useful on a laptop, to suspend activity and reduce battery drain, while preserving the current state of ones sessions and avoiding having to logout or shutdown?

Yes, the original purpose for the inclusion of the freezer code was to support suspend-resume (mainly for laptops, but suspend-resume could be useful in other circumstances too, see the FAQ).

Quoted 2 lines

Is it useful for quietting a system down before doing hot plug or unplug of key components, such as processors and memory?

Yes, the freezer is (proposed to be, at least) moving on from being merely a suspend-resume-only thing to other usage scenarios, such as kprobes and hotlpug.

#32 lore raw

Re: [RFC][PATCH -mm 2/3] freezer: Introduce freezer_flags

In reply to Rafael J. Wysocki

Hi Rafael,

include/linux/freezer.h line 15

Quoted 9 lines
include/linux/freezer.h +9 -0
+/*
+ *     Per task flags used by the freezer
+ *
+ *     They should not be referred to directly outside of this file.
+ */
+#define TFF_NOFREEZE   0       /* task should not be frozen */
+#define TFF_FREEZE     8       /* task should go to the refrigerator ASAP */
+#define TFF_SKIP       9       /* do not count this task as freezable */
+#define TFF_FROZEN     10      /* task is frozen */

Aren't NOFREEZE and SKIP doing the same thing? One of them appears superfluous. I'm looking at 21-rc6-mm1 and vfork(2) seems to be its only user. Seeing how vfork(2) used it, can't the call to freezer_do_not_count() be replaced with a call to freezer_exempt()? Similarly, the freezer_count() after the wait_for_completion might just as well be a clear of the NOFREEZE bit followed by a try_to_freeze(). Could you please explain the rationale behind the SKIP flag?

I do see that SKIP seems to be relevant for only userspace threads and presumably only kernel threads are allowed to set NOFREEZE, but why this distinction between the two?

Also, I do have several gripes against the naming of some of these functions:

Quoted 1 line

static inline int freezing(struct task_struct *p)

This could be called task_should_freeze().

Quoted 5 lines

/*
- * Sometimes we may need to cancel the previous 'freeze' request
+ * Cancel the previous 'freeze' request
*/
static inline void do_not_freeze(struct task_struct *p)

This definitely needs to be undo_freeze() or unfreeze(). do_not_freeze() sounds like what freeze_exempt() does.

Quoted 1 line

static inline void frozen_process(struct task_struct *p)

frozen_process() sounds like what frozen() is supposed to do. This could instead be mark_task_frozen(), or even mark_frozen(), because only the current task can ever mark *itself* frozen before freezing itself.

Quoted 2 lines

static inline void freezer_do_not_count(void)
static inline void freezer_count(void)

These could be called freezer_skip() and freezer_do_not_skip(). Better to stick to consistent naming / terminology.

Cheers,
Satyam

#33 lore raw

Re: [RFC][PATCH -mm 2/3] freezer: Introduce freezer_flags

Hi Satyam,

Quoted 13 lines · Satyam Sharma

Hi Rafael,

include/linux/freezer.h line 15

Quoted 10 lines
include/linux/freezer.h +7 -0
+/*
+ *     Per task flags used by the freezer
+ *
+ *     They should not be referred to directly outside of this file.
+ */
+#define TFF_NOFREEZE   0       /* task should not be frozen */
+#define TFF_FREEZE     8       /* task should go to the refrigerator ASAP 

*/

include/linux/freezer.h +2 -0
+#define TFF_SKIP       9       /* do not count this task as freezable */
+#define TFF_FROZEN     10      /* task is frozen */
Quoted 8 lines

Aren't NOFREEZE and SKIP doing the same thing? One of them appears superfluous. I'm looking at 21-rc6-mm1 and vfork(2) seems to be its only user. Seeing how vfork(2) used it, can't the call to freezer_do_not_count() be replaced with a call to freezer_exempt()? Similarly, the freezer_count() after the wait_for_completion might just as well be a clear of the NOFREEZE bit followed by a try_to_freeze(). Could you please explain the rationale behind the SKIP flag?

The difference between the NOFREEZE and the SKIP flag is a subtle one.

When a task (say p) sets it's NOFREEZE flag, it tells the freezer not to consider it for freezing. Which means freezeable(p) will return 0. So the freezer will not even mark it for freezing.

However, when a task sets it SKIP flag, it tells the freezer - "I might block at a safe place. So when you are counting the processes which have been marked as freezeable, but have not frozen yet, please don't count me in. IOW, please skip me."
Thus such a task can still be marked for freezing.

The typical usage is
freezer_do_not_count(current);
/* currents goes to an uninterruptible sleep, like wait_for_completion. */ freezer_count(current);

Once the task wakes up from it's uninterruptible sleep, it will call freezer_count which in turn calls try_to_freeze. If the task was marked for freezing, it will be frozen now.

You may want to check the thread http://lkml.org/lkml/2007/2/18/47
on how it came into existance.

Quoted 3 lines

Cheers,
Satyam

Regards
gautham.

Signature · 5 lines
Gautham R Shenoy
Linux Technology Center
IBM India.
"Freedom comes with a price tag of responsibility, which is still a bargain,
because Freedom is priceless!"
#34 lore raw

Re: [RFC][PATCH -mm 2/3] freezer: Introduce freezer_flags

Quoted 41 lines · Gautham R Shenoy

Hi Satyam,

Quoted 13 lines · Satyam Sharma

Hi Rafael,

include/linux/freezer.h line 15

Quoted 10 lines
include/linux/freezer.h +7 -0
+/*
+ *     Per task flags used by the freezer
+ *
+ *     They should not be referred to directly outside of this file.
+ */
+#define TFF_NOFREEZE   0       /* task should not be frozen */
+#define TFF_FREEZE     8       /* task should go to the refrigerator ASAP 

*/

include/linux/freezer.h +2 -0
+#define TFF_SKIP       9       /* do not count this task as freezable */
+#define TFF_FROZEN     10      /* task is frozen */
Quoted 8 lines

Aren't NOFREEZE and SKIP doing the same thing? One of them appears superfluous. I'm looking at 21-rc6-mm1 and vfork(2) seems to be its only user. Seeing how vfork(2) used it, can't the call to freezer_do_not_count() be replaced with a call to freezer_exempt()? Similarly, the freezer_count() after the wait_for_completion might just as well be a clear of the NOFREEZE bit followed by a try_to_freeze(). Could you please explain the rationale behind the SKIP flag?

The difference between the NOFREEZE and the SKIP flag is a subtle one.

When a task (say p) sets it's NOFREEZE flag, it tells the freezer not to consider it for freezing. Which means freezeable(p) will return 0. So the freezer will not even mark it for freezing.

However, when a task sets it SKIP flag, it tells the freezer - "I might block at a safe place. So when you are counting the processes which have been marked as freezeable, but have not frozen yet, please don't count me in. IOW, please skip me."
Thus such a task can still be marked for freezing.

The typical usage is
freezer_do_not_count(current);
/* currents goes to an uninterruptible sleep, like wait_for_completion. */ freezer_count(current);

Quoted 6 lines

Once the task wakes up from it's uninterruptible sleep, it will call freezer_count which in turn calls try_to_freeze. If the task was marked for freezing, it will be frozen now.

You may want to check the thread http://lkml.org/lkml/2007/2/18/47
on how it came into existance.

Very well explained. Thanks!

Greetings,
Rafael

#35 lore raw

Re: [RFC][PATCH -mm 2/3] freezer: Introduce freezer_flags

In reply to Satyam Sharma

Hi,

Quoted 2 lines · Satyam Sharma

Hi Rafael,

[--snip--]

Quoted 23 lines

Also, I do have several gripes against the naming of some of these functions:

Quoted 1 line

static inline int freezing(struct task_struct *p)

This could be called task_should_freeze().

Quoted 5 lines

/*
- * Sometimes we may need to cancel the previous 'freeze' request
+ * Cancel the previous 'freeze' request
*/
static inline void do_not_freeze(struct task_struct *p)

This definitely needs to be undo_freeze() or unfreeze(). do_not_freeze() sounds like what freeze_exempt() does.

Quoted 1 line

static inline void frozen_process(struct task_struct *p)

frozen_process() sounds like what frozen() is supposed to do. This could instead be mark_task_frozen(), or even mark_frozen(), because only the current task can ever mark *itself* frozen before freezing itself.

Quoted 5 lines
Quoted 2 lines

static inline void freezer_do_not_count(void)
static inline void freezer_count(void)

These could be called freezer_skip() and freezer_do_not_skip(). Better to stick to consistent naming / terminology.

Well, I agree that the names are not the best ones, but I'd like to avoid changing the existing names in this particular patch.

We can change them later, on top of it.

Greetings,
Rafael

#36 lore raw

Re: [RFC][PATCH -mm 2/3] freezer: Introduce freezer_flags

In reply to Rafael J. Wysocki

Hi!

Quoted 4 lines

From: Rafael J. Wysocki <[email protected]>

Move all of the freezer-related flags to a separate field in task_struct and introduce functions to operate them using set_bit() etc.

Looks sane to me.

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

Re: [RFC][PATCH -mm 2/3] freezer: Introduce freezer_flags

In reply to Rafael J. Wysocki

include/linux/freezer.h static inline int thaw_process(struct ta · line 105

Quoted 9 lines · Rafael J. Wysocki
include/linux/freezer.h +2 -2
@@ -63,9 +100,9 @@ static inline int thaw_process(struct ta
  */
 static inline void frozen_process(struct task_struct *p)
 {
-	p->flags |= PF_FROZEN;
+	set_frozen_flag(p);
 	wmb();
-	clear_tsk_thread_flag(p, TIF_FREEZE);
+	do_not_freeze(p);

We may want to rename do_not_freeze to something else. It kind of looks weird calling do_not_freeze(p) after setting the frozen flag!

Probably, just a matter of taste :-)

Review static inline int thaw_process(struct ta · line 107

Quoted 2 lines
(quoted diff) +0 -0
 }
 

Thanks and Regards
gautham.

Signature · 5 lines
Gautham R Shenoy
Linux Technology Center
IBM India.
"Freedom comes with a price tag of responsibility, which is still a bargain,
because Freedom is priceless!"
#38 lore raw

Re: [RFC][PATCH -mm 2/3] freezer: Introduce freezer_flags

Quoted 14 lines · Gautham R Shenoy

include/linux/freezer.h static inline int thaw_process(struct ta · line 105

Quoted 9 lines · Rafael J. Wysocki
include/linux/freezer.h +2 -2
@@ -63,9 +100,9 @@ static inline int thaw_process(struct ta
  */
 static inline void frozen_process(struct task_struct *p)
 {
-	p->flags |= PF_FROZEN;
+	set_frozen_flag(p);
 	wmb();
-	clear_tsk_thread_flag(p, TIF_FREEZE);
+	do_not_freeze(p);

We may want to rename do_not_freeze to something else. It kind of looks weird calling do_not_freeze(p) after setting the frozen flag!

Quoted 1 line

Probably, just a matter of taste :-)

Hmm, I wanted to avoid changing the existing names in this patch, but in this particular case you are obviously right. I'll change that to clear_freeze_flag().

Greetings,
Rafael

#39 lore raw

Re: [RFC][PATCH -mm 2/3] freezer: Introduce freezer_flags

In reply to Rafael J. Wysocki
Quoted 9 lines · Rafael J. Wysocki

Move all of the freezer-related flags to a separate field in task_struct and introduce functions to operate them using set_bit() etc.

[...snip...]

kernel/fork.c static inline void copy_flags(unsigned l · line 928

Quoted 8 lines
kernel/fork.c +1 -1
@@ -922,7 +922,7 @@ static inline void copy_flags(unsigned l
 {
 	unsigned long new_flags = p->flags;
 
-	new_flags &= ~(PF_SUPERPRIV | PF_NOFREEZE);
+	new_flags &= ~PF_SUPERPRIV;
 	new_flags |= PF_FORKNOEXEC;
 	new_flags |= PF_STARTING;
 	p->flags = new_flags;

OK, but you forgot to clear p->freezer_flags on copy_process()->dup_task_struct() path ?

Oleg.

#40 lore raw

Re: [RFC][PATCH -mm 2/3] freezer: Introduce freezer_flags

Quoted 13 lines · Oleg Nesterov
Quoted 12 lines · Rafael J. Wysocki

Move all of the freezer-related flags to a separate field in task_struct and introduce functions to operate them using set_bit() etc.

[...snip...]

kernel/fork.c +0 -0
@@ -922,7 +922,7 @@ static inline void copy_flags(unsigned l
 {
 	unsigned long new_flags = p->flags;
 
Quoted 8 lines

kernel/fork.c static inline void copy_flags(unsigned l · line 928

Quoted 5 lines
kernel/fork.c +1 -1
-	new_flags &= ~(PF_SUPERPRIV | PF_NOFREEZE);
+	new_flags &= ~PF_SUPERPRIV;
 	new_flags |= PF_FORKNOEXEC;
 	new_flags |= PF_STARTING;
 	p->flags = new_flags;

OK, but you forgot to clear p->freezer_flags on copy_process()->dup_task_struct() path ?

Yes, thanks for pointing that out.

Should I clear it in dup_task_struct() or is there a better place?

#41 lore raw

Re: [RFC][PATCH -mm 2/3] freezer: Introduce freezer_flags

Quoted 23 lines · Rafael J. Wysocki
Quoted 13 lines · Oleg Nesterov
Quoted 12 lines · Rafael J. Wysocki

Move all of the freezer-related flags to a separate field in task_struct and introduce functions to operate them using set_bit() etc.

[...snip...]

kernel/fork.c +0 -0
@@ -922,7 +922,7 @@ static inline void copy_flags(unsigned l
 {
 	unsigned long new_flags = p->flags;
 
Quoted 8 lines

kernel/fork.c static inline void copy_flags(unsigned l · line 928

Quoted 5 lines
kernel/fork.c +1 -1
-	new_flags &= ~(PF_SUPERPRIV | PF_NOFREEZE);
+	new_flags &= ~PF_SUPERPRIV;
 	new_flags |= PF_FORKNOEXEC;
 	new_flags |= PF_STARTING;
 	p->flags = new_flags;

OK, but you forgot to clear p->freezer_flags on copy_process()->dup_task_struct() path ?

Quoted 1 line

Yes, thanks for pointing that out.

That reminds me, shouldn't we set the child's TFF_FREEZE flag if the parent is freezing or frozen?

Quoted 2 lines

Should I clear it in dup_task_struct() or is there a better place?

Thanks and Regards
gautham.

Signature · 5 lines
Gautham R Shenoy
Linux Technology Center
IBM India.
"Freedom comes with a price tag of responsibility, which is still a bargain,
because Freedom is priceless!"
#42 lore raw

Re: [RFC][PATCH -mm 2/3] freezer: Introduce freezer_flags

Quoted 24 lines · Gautham R Shenoy
Quoted 23 lines · Rafael J. Wysocki
Quoted 21 lines · Oleg Nesterov
Quoted 9 lines · Rafael J. Wysocki

Move all of the freezer-related flags to a separate field in task_struct and introduce functions to operate them using set_bit() etc.

[...snip...]

kernel/fork.c static inline void copy_flags(unsigned l · line 928

Quoted 8 lines
kernel/fork.c +1 -1
@@ -922,7 +922,7 @@ static inline void copy_flags(unsigned l
 {
 	unsigned long new_flags = p->flags;
 
-	new_flags &= ~(PF_SUPERPRIV | PF_NOFREEZE);
+	new_flags &= ~PF_SUPERPRIV;
 	new_flags |= PF_FORKNOEXEC;
 	new_flags |= PF_STARTING;
 	p->flags = new_flags;

OK, but you forgot to clear p->freezer_flags on copy_process()->dup_task_struct() path ?

Quoted 4 lines
Quoted 1 line

Yes, thanks for pointing that out.

That reminds me, shouldn't we set the child's TFF_FREEZE flag if the parent is freezing or frozen?

Well, usually we'll want to freeze the child too in such cases, but I'm not sure if that's always true.

Greetings,
Rafael

#43 lore raw

Re: [RFC][PATCH -mm 2/3] freezer: Introduce freezer_flags

In reply to Rafael J. Wysocki
Quoted 2 lines · Rafael J. Wysocki

Should I clear it in dup_task_struct() or is there a better place?

I personally think we should do this in dup_task_struct(). In fact, I believe it is better to replace the

	*tsk = *orig;

with some helper (like setup_thread_stack() below), and that helper clears ->freezer_flags. Say, copy_task_struct().

Oleg.

#44 lore raw

Re: [RFC][PATCH -mm 2/3] freezer: Introduce freezer_flags

Quoted 11 lines · Oleg Nesterov
Quoted 2 lines · Rafael J. Wysocki

Should I clear it in dup_task_struct() or is there a better place?

I personally think we should do this in dup_task_struct(). In fact, I believe it is better to replace the

	*tsk = *orig;

with some helper (like setup_thread_stack() below), and that helper clears ->freezer_flags. Say, copy_task_struct().

Hmm, wouldn't that be overkill? copy_task_struct() would have to do *tsk = *orig anyway, and we only need to clear one field apart from this.

Some other fields are cleared towards the end of dup_task_struct(), so perhaps we could clear freezer_flags in there too?

Rafael

#45 lore raw

Re: [RFC][PATCH -mm 2/3] freezer: Introduce freezer_flags

Quoted 11 lines · Rafael J. Wysocki
Quoted 9 lines · Oleg Nesterov
Quoted 2 lines · Rafael J. Wysocki

Should I clear it in dup_task_struct() or is there a better place?

I personally think we should do this in dup_task_struct(). In fact, I believe it is better to replace the

	*tsk = *orig;
Quoted 8 lines
Quoted 2 lines

with some helper (like setup_thread_stack() below), and that helper clears ->freezer_flags. Say, copy_task_struct().

Hmm, wouldn't that be overkill? copy_task_struct() would have to do *tsk = *orig anyway, and we only need to clear one field apart from this.

Some other fields are cleared towards the end of dup_task_struct(), so perhaps we could clear freezer_flags in there too?

Yes. And I strongly believe it is bad we don't have the helper which does some random stuf like "p->did_exec = 0".

The same for thread_info. Could you answer quickly where do we clear TIF_FREEZE currently? We don't.

Oleg.

#46 lore raw

Re: [RFC][PATCH -mm 2/3] freezer: Introduce freezer_flags

Quoted 18 lines · Oleg Nesterov
Quoted 17 lines · Rafael J. Wysocki
Quoted 11 lines · Oleg Nesterov
Quoted 2 lines · Rafael J. Wysocki

Should I clear it in dup_task_struct() or is there a better place?

I personally think we should do this in dup_task_struct(). In fact, I believe it is better to replace the

	*tsk = *orig;

with some helper (like setup_thread_stack() below), and that helper clears ->freezer_flags. Say, copy_task_struct().

Hmm, wouldn't that be overkill? copy_task_struct() would have to do *tsk = *orig anyway, and we only need to clear one field apart from this.

Quoted 8 lines
Quoted 2 lines

Some other fields are cleared towards the end of dup_task_struct(), so perhaps we could clear freezer_flags in there too?

Yes. And I strongly believe it is bad we don't have the helper which does some random stuf like "p->did_exec = 0".

The same for thread_info. Could you answer quickly where do we clear TIF_FREEZE currently? We don't.

Right.

That said, I think the right thing to do would be to add just one line 'freezer_flags = 0' to dup_task_struct() directly in this patch and then move all of the random stuff (including this line) to the helper in a separate patch.

I mean, the introduction of the helper seems to be logically unrelated to the other things this patch does, so it should go in another patch IMHO.

Greetings,
Rafael

#47 lore raw

RFC PATCH 3/3 freezer: Fix problem with kthread_stop

In reply to Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

Fix the problem with kthread_stop() that causes the freezer to fail if a freezable thread is attempting to stop a frozen one and that may cause the freezer to fail if the thread being stopped is freezable and try_to_freeze_tasks() is running concurrently with kthread_stop().

---
 kernel/kthread.c |    9 +++++++++
 1 file changed, 9 insertions(+)
kernel/kthread.c +9 -0
Index: linux-2.6.21-rc6-mm1/kernel/kthread.c
===================================================================
@@ -13,6 +13,7 @@
 #include <linux/file.h>
 #include <linux/module.h>
 #include <linux/mutex.h>
+#include <linux/freezer.h>
 #include <asm/semaphore.h>
 
 /*
@@ -232,6 +233,14 @@ int kthread_stop(struct task_struct *k)
 
 	/* Now set kthread_should_stop() to true, and wake it up. */
 	kthread_stop_info.k = k;
+	if (!freezer_should_exempt(current)) {
+		/* We are freezable, so we must make sure that the thread being
+		 * stopped is not frozen and will not be frozen until it dies
+		 */
+		freezer_exempt(k);
+		if (frozen(k))
+			clear_frozen_flag(k);
+	}
 	wake_up_process(k);
 	put_task_struct(k);
 
#48 lore raw

Re: [RFC][PATCH -mm 3/3] freezer: Fix problem with kthread_stop

Hi!

Quoted 4 lines

Fix the problem with kthread_stop() that causes the freezer to fail if a freezable thread is attempting to stop a frozen one and that may cause the freezer to fail if the thread being stopped is freezable and try_to_freeze_tasks() is running concurrently with kthread_stop().

Parse error.

Quoted 9 lines

Index: linux-2.6.21-rc6-mm1/kernel/kthread.c
===================================================================

kernel/kthread.c +1 -0
@@ -232,6 +233,14 @@ int kthread_stop(struct task_struct *k)
 
 	/* Now set kthread_should_stop() to true, and wake it up. */
 	kthread_stop_info.k = k;
+	if (!freezer_should_exempt(current)) {

kernel/kthread.c int kthread_stop(struct task_struct *k) · line 246

Quoted 10 lines
kernel/kthread.c +7 -0
+		/* We are freezable, so we must make sure that the thread being
+		 * stopped is not frozen and will not be frozen until it dies
+		 */
+		freezer_exempt(k);
+		if (frozen(k))
+			clear_frozen_flag(k);
+	}
 	wake_up_process(k);
 	put_task_struct(k);
 

Do we need to take some locks to access other process' flags? Or do frozen_exempt() etc take enough locks, already?
Pavel

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

Re: [RFC][PATCH -mm 3/3] freezer: Fix problem with kthread_stop

Hi,

Quoted 8 lines · Pavel Machek

Hi!

Quoted 4 lines

Fix the problem with kthread_stop() that causes the freezer to fail if a freezable thread is attempting to stop a frozen one and that may cause the freezer to fail if the thread being stopped is freezable and try_to_freeze_tasks() is running concurrently with kthread_stop().

Parse error.

OK, will fix.

Quoted 6 lines
Quoted 6 lines

Index: linux-2.6.21-rc6-mm1/kernel/kthread.c
===================================================================

kernel/kthread.c +0 -0
@@ -232,6 +233,14 @@ int kthread_stop(struct task_struct *k)
 
Quoted 16 lines

kernel/kthread.c int kthread_stop(struct task_struct *k) · line 246

Quoted 13 lines
kernel/kthread.c +8 -0
 	/* Now set kthread_should_stop() to true, and wake it up. */
 	kthread_stop_info.k = k;
+	if (!freezer_should_exempt(current)) {
+		/* We are freezable, so we must make sure that the thread being
+		 * stopped is not frozen and will not be frozen until it dies
+		 */
+		freezer_exempt(k);
+		if (frozen(k))
+			clear_frozen_flag(k);
+	}
 	wake_up_process(k);
 	put_task_struct(k);
 

Do we need to take some locks to access other process' flags? Or do frozen_exempt() etc take enough locks, already?

After the previous patch we only use set_bit(), clear_bit() and test_bit() to access freezer_falgs, so no special locking is needed to protect them.

Greetings,
Rafael

#50 lore raw

Re: [RFC][PATCH -mm 3/3] freezer: Fix problem with kthread_stop

In reply to Rafael J. Wysocki
Quoted 26 lines · Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

Fix the problem with kthread_stop() that causes the freezer to fail if a freezable thread is attempting to stop a frozen one and that may cause the freezer to fail if the thread being stopped is freezable and try_to_freeze_tasks() is running concurrently with kthread_stop().

---
 kernel/kthread.c |    9 +++++++++
 1 file changed, 9 insertions(+)

Index: linux-2.6.21-rc6-mm1/kernel/kthread.c
===================================================================

kernel/kthread.c +1 -0
@@ -13,6 +13,7 @@
 #include <linux/file.h>
 #include <linux/module.h>
 #include <linux/mutex.h>
+#include <linux/freezer.h>
 #include <asm/semaphore.h>
 
 /*
@@ -232,6 +233,14 @@ int kthread_stop(struct task_struct *k)
 

kernel/kthread.c int kthread_stop(struct task_struct *k) · line 243

Quoted 10 lines
kernel/kthread.c +8 -0
 	/* Now set kthread_should_stop() to true, and wake it up. */
 	kthread_stop_info.k = k;
+	if (!freezer_should_exempt(current)) {
+		/* We are freezable, so we must make sure that the thread being
+		 * stopped is not frozen and will not be frozen until it dies
+		 */
+		freezer_exempt(k);
+		if (frozen(k))
+			clear_frozen_flag(k);
+	}

I'm trying hard to convince myself that this will work. May be I am missing something here, but I find a potential race window (very small though) when k is entering the refrigerator.

Here's how.

kthread_stop(k) k->refrigerator()
---------------------------------------------------------------------

						task_lock(k);
						1) check_if_exempted();
						/* not exempted. So 
						 * we will freeze
						 * ourself.
						 */

2) freezer_exempt(k);

3) if(frozen(k))
/* No, we're not yet frozen. So we
* don't clear_frozen_flag(k) here
*/

						4) frozen_process(k);
						   task_unlock(k);
						
						5) for(;;) {
						 set_current_state(UNINTERRUPTIBLE);
						  if(!frozen_process(k))
						  /* k is frozen. We
						   * don't break :( 
						   */
						 
						  	schedule();
						}

kernel/kthread.c int kthread_stop(struct task_struct *k) · line 246

Quoted 3 lines
kernel/kthread.c +0 -0
 	wake_up_process(k);
 	put_task_struct(k);
 

Thus the freezer can still fail, no?
IMO, we need the to take the task_lock for k here. Something like

kernel/kthread.c int kthread_stop(struct task_struct *k) · line 236

Quoted 1 line
kernel/kthread.c +1 -0
+	if (!freezer_should_exempt(current)) {
		task_lock(k);

kernel/kthread.c int kthread_stop(struct task_struct *k) · line 242

Quoted 6 lines
kernel/kthread.c +6 -0
+		/* We are freezable, so we must make sure that the thread being
+		 * stopped is not frozen and will not be frozen until it dies
+		 */
+		freezer_exempt(k);
+		if (frozen(k))
+			clear_frozen_flag(k);
		task_unlock(k);

kernel/kthread.c int kthread_stop(struct task_struct *k) · line 243

Quoted 1 line
kernel/kthread.c +1 -0
+	}

Thanks and Regards
gautham.

Signature · 5 lines
Gautham R Shenoy
Linux Technology Center
IBM India.
"Freedom comes with a price tag of responsibility, which is still a bargain,
because Freedom is priceless!"
#51 lore raw

Re: [RFC][PATCH -mm 3/3] freezer: Fix problem with kthread_stop

Quoted 26 lines · Gautham R Shenoy

kernel/kthread.c int kthread_stop(struct task_struct *k) · line 243

Quoted 13 lines · Rafael J. Wysocki

/*

kernel/kthread.c +8 -0
@@ -232,6 +233,14 @@ int kthread_stop(struct task_struct *k)
 
 	/* Now set kthread_should_stop() to true, and wake it up. */
 	kthread_stop_info.k = k;
+	if (!freezer_should_exempt(current)) {
+		/* We are freezable, so we must make sure that the thread being
+		 * stopped is not frozen and will not be frozen until it dies
+		 */
+		freezer_exempt(k);
+		if (frozen(k))
+			clear_frozen_flag(k);
+	}

I'm trying hard to convince myself that this will work. May be I am missing something here, but I find a potential race window (very small though) when k is entering the refrigerator.

[... snip ... ]

IMO, we need the to take the task_lock for k here. Something like

kernel/kthread.c int kthread_stop(struct task_struct *k) · line 236

Quoted 1 line
kernel/kthread.c +1 -0
+	if (!freezer_should_exempt(current)) {
		task_lock(k);
Quoted 8 lines

kernel/kthread.c int kthread_stop(struct task_struct *k) · line 242

Quoted 6 lines
kernel/kthread.c +6 -0
+		/* We are freezable, so we must make sure that the thread being
+		 * stopped is not frozen and will not be frozen until it dies
+		 */
+		freezer_exempt(k);
+		if (frozen(k))
+			clear_frozen_flag(k);
		task_unlock(k);

kernel/kthread.c int kthread_stop(struct task_struct *k) · line 243

Quoted 1 line
kernel/kthread.c +1 -0
+	}

Well, probably I missed something, but why can't we do

	if (!freezer_should_exempt(current)) {
		freezer_exempt(k);
		thaw_process(k);
	}

?

thaw_process(k) is properly serialized with refrigerator(), and it checks frozen(k). We can make an extra wake_up, but this should not matter.

Rafael, please check the recent changes in kthread.c, kthread_stop() was reworked, we don't have kthread_stop_info any longer.

Oleg.

#52 lore raw

Re: [RFC][PATCH -mm 3/3] freezer: Fix problem with kthread_stop

Quoted 38 lines · Oleg Nesterov
Quoted 26 lines · Gautham R Shenoy

kernel/kthread.c int kthread_stop(struct task_struct *k) · line 243

Quoted 13 lines · Rafael J. Wysocki

/*

kernel/kthread.c +8 -0
@@ -232,6 +233,14 @@ int kthread_stop(struct task_struct *k)
 
 	/* Now set kthread_should_stop() to true, and wake it up. */
 	kthread_stop_info.k = k;
+	if (!freezer_should_exempt(current)) {
+		/* We are freezable, so we must make sure that the thread being
+		 * stopped is not frozen and will not be frozen until it dies
+		 */
+		freezer_exempt(k);
+		if (frozen(k))
+			clear_frozen_flag(k);
+	}

I'm trying hard to convince myself that this will work. May be I am missing something here, but I find a potential race window (very small though) when k is entering the refrigerator.

[... snip ... ]

IMO, we need the to take the task_lock for k here. Something like

kernel/kthread.c int kthread_stop(struct task_struct *k) · line 236

Quoted 1 line
kernel/kthread.c +1 -0
+	if (!freezer_should_exempt(current)) {
		task_lock(k);
Quoted 8 lines

kernel/kthread.c int kthread_stop(struct task_struct *k) · line 242

Quoted 6 lines
kernel/kthread.c +6 -0
+		/* We are freezable, so we must make sure that the thread being
+		 * stopped is not frozen and will not be frozen until it dies
+		 */
+		freezer_exempt(k);
+		if (frozen(k))
+			clear_frozen_flag(k);
		task_unlock(k);

kernel/kthread.c int kthread_stop(struct task_struct *k) · line 243

Quoted 1 line
kernel/kthread.c +1 -0
+	}

Well, probably I missed something, but why can't we do

Quoted 8 lines
	if (!freezer_should_exempt(current)) {
		freezer_exempt(k);
		thaw_process(k);
	}

?

thaw_process(k) is properly serialized with refrigerator(), and it checks frozen(k). We can make an extra wake_up, but this should not matter.

Yes, I think you're right.

Quoted 2 lines

Rafael, please check the recent changes in kthread.c, kthread_stop() was reworked, we don't have kthread_stop_info any longer.

OK, I will, thanks.

Greetings,
Rafael

#53 lore raw

Re: [RFC][PATCH -mm 3/3] freezer: Fix problem with kthread_stop

In reply to Gautham R Shenoy
Quoted 80 lines · Gautham R Shenoy
Quoted 26 lines · Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

Fix the problem with kthread_stop() that causes the freezer to fail if a freezable thread is attempting to stop a frozen one and that may cause the freezer to fail if the thread being stopped is freezable and try_to_freeze_tasks() is running concurrently with kthread_stop().

---
 kernel/kthread.c |    9 +++++++++
 1 file changed, 9 insertions(+)

Index: linux-2.6.21-rc6-mm1/kernel/kthread.c
===================================================================

kernel/kthread.c +1 -0
@@ -13,6 +13,7 @@
 #include <linux/file.h>
 #include <linux/module.h>
 #include <linux/mutex.h>
+#include <linux/freezer.h>
 #include <asm/semaphore.h>
 
 /*
@@ -232,6 +233,14 @@ int kthread_stop(struct task_struct *k)
 

kernel/kthread.c int kthread_stop(struct task_struct *k) · line 243

Quoted 10 lines
kernel/kthread.c +8 -0
 	/* Now set kthread_should_stop() to true, and wake it up. */
 	kthread_stop_info.k = k;
+	if (!freezer_should_exempt(current)) {
+		/* We are freezable, so we must make sure that the thread being
+		 * stopped is not frozen and will not be frozen until it dies
+		 */
+		freezer_exempt(k);
+		if (frozen(k))
+			clear_frozen_flag(k);
+	}

I'm trying hard to convince myself that this will work. May be I am missing something here, but I find a potential race window (very small though) when k is entering the refrigerator.

Here's how.

kthread_stop(k) k->refrigerator()
---------------------------------------------------------------------

						task_lock(k);
						1) check_if_exempted();
						/* not exempted. So 
						 * we will freeze
						 * ourself.
						 */

2) freezer_exempt(k);

3) if(frozen(k))
/* No, we're not yet frozen. So we
* don't clear_frozen_flag(k) here
*/

						4) frozen_process(k);
						   task_unlock(k);
						
						5) for(;;) {
						 set_current_state(UNINTERRUPTIBLE);
						  if(!frozen_process(k))
						  /* k is frozen. We
						   * don't break :( 
						   */
						 
						  	schedule();
						}

kernel/kthread.c int kthread_stop(struct task_struct *k) · line 246

Quoted 3 lines
kernel/kthread.c +0 -0
 	wake_up_process(k);
 	put_task_struct(k);
 

Thus the freezer can still fail, no?
IMO, we need the to take the task_lock for k here. Something like

kernel/kthread.c int kthread_stop(struct task_struct *k) · line 236

Quoted 1 line
kernel/kthread.c +1 -0
+	if (!freezer_should_exempt(current)) {
		task_lock(k);
Quoted 8 lines

kernel/kthread.c int kthread_stop(struct task_struct *k) · line 242

Quoted 6 lines
kernel/kthread.c +6 -0
+		/* We are freezable, so we must make sure that the thread being
+		 * stopped is not frozen and will not be frozen until it dies
+		 */
+		freezer_exempt(k);
+		if (frozen(k))
+			clear_frozen_flag(k);
		task_unlock(k);

kernel/kthread.c int kthread_stop(struct task_struct *k) · line 243

Quoted 1 line
kernel/kthread.c +1 -0
+	}

Yes, that's correct. We need to take task_lock() to avoid the race with refrigerator().

I'll fix the patch.

BTW, I think I should rediff the entire series against -mm with your patch from
http://lkml.org/lkml/2007/4/23/98 applied.

Greetings,
Rafael

#54 lore raw

Re: [RFC][PATCH -mm 3/3] freezer: Fix problem with kthread_stop

Quoted 11 lines · Rafael J. Wysocki
Quoted 9 lines · Gautham R Shenoy

kernel/kthread.c int kthread_stop(struct task_struct *k) · line 236

Quoted 1 line
kernel/kthread.c +1 -0
+	if (!freezer_should_exempt(current)) {
		task_lock(k);

kernel/kthread.c int kthread_stop(struct task_struct *k) · line 242

Quoted 6 lines
kernel/kthread.c +6 -0
+		/* We are freezable, so we must make sure that the thread being
+		 * stopped is not frozen and will not be frozen until it dies
+		 */
+		freezer_exempt(k);
+		if (frozen(k))
+			clear_frozen_flag(k);
		task_unlock(k);
Quoted 4 lines
Quoted 1 line

kernel/kthread.c int kthread_stop(struct task_struct *k) · line 243

Quoted 1 line
kernel/kthread.c +1 -0
+	}

Yes, that's correct. We need to take task_lock() to avoid the race with refrigerator().

Even if we use thaw_task() ?

Even if I am wrong, I think we should not use task_lock() for the freezing related code, except in freezer.[ch]

Note also that without CONFIG_FREEZER freezer_should_exempt() == 0, so we will do unneeded task_lock/task_unlock.

Oleg.

#55 lore raw

Re: [RFC][PATCH -mm 3/3] freezer: Fix problem with kthread_stop

Quoted 12 lines · Oleg Nesterov
Quoted 11 lines · Rafael J. Wysocki
Quoted 9 lines · Gautham R Shenoy

kernel/kthread.c int kthread_stop(struct task_struct *k) · line 236

Quoted 1 line
kernel/kthread.c +1 -0
+	if (!freezer_should_exempt(current)) {
		task_lock(k);

kernel/kthread.c int kthread_stop(struct task_struct *k) · line 242

Quoted 6 lines
kernel/kthread.c +6 -0
+		/* We are freezable, so we must make sure that the thread being
+		 * stopped is not frozen and will not be frozen until it dies
+		 */
+		freezer_exempt(k);
+		if (frozen(k))
+			clear_frozen_flag(k);
		task_unlock(k);
Quoted 6 lines
Quoted 4 lines
Quoted 1 line

kernel/kthread.c int kthread_stop(struct task_struct *k) · line 243

Quoted 1 line
kernel/kthread.c +1 -0
+	}

Yes, that's correct. We need to take task_lock() to avoid the race with refrigerator().

Even if we use thaw_task() ?

I don't think so. As you correctly pointed out, thaw_task() is race free w.r.t the refrigerator().

Quoted 9 lines

Even if I am wrong, I think we should not use task_lock() for the freezing related code, except in freezer.[ch]

Note also that without CONFIG_FREEZER freezer_should_exempt() == 0, so we will do unneeded task_lock/task_unlock.

Oleg.

Thanks and Regards
gautham.

Signature · 5 lines
Gautham R Shenoy
Linux Technology Center
IBM India.
"Freedom comes with a price tag of responsibility, which is still a bargain,
because Freedom is priceless!"
#56 lore raw

Re: [RFC][PATCH -mm 3/3] freezer: Fix problem with kthread_stop

Quoted 18 lines · Gautham R Shenoy
Quoted 17 lines · Oleg Nesterov
Quoted 15 lines · Rafael J. Wysocki
Quoted 10 lines · Gautham R Shenoy

kernel/kthread.c int kthread_stop(struct task_struct *k) · line 236

Quoted 1 line
kernel/kthread.c +1 -0
+	if (!freezer_should_exempt(current)) {
		task_lock(k);

kernel/kthread.c int kthread_stop(struct task_struct *k) · line 242

Quoted 6 lines
kernel/kthread.c +6 -0
+		/* We are freezable, so we must make sure that the thread being
+		 * stopped is not frozen and will not be frozen until it dies
+		 */
+		freezer_exempt(k);
+		if (frozen(k))
+			clear_frozen_flag(k);
		task_unlock(k);

kernel/kthread.c int kthread_stop(struct task_struct *k) · line 243

Quoted 1 line
kernel/kthread.c +1 -0
+	}

Yes, that's correct. We need to take task_lock() to avoid the race with refrigerator().

Quoted 4 lines
Quoted 1 line

Even if we use thaw_task() ?

I don't think so. As you correctly pointed out, thaw_task() is race free w.r.t the refrigerator().

Agreed.

Rafael

#57 lore raw

Re: [RFC PATCH(experimental) 2/2] Fix freezer-kthread_stop race

In reply to Gautham R Shenoy
Quoted 23 lines · Gautham R Shenoy
Quoted 21 lines · Rafael J. Wysocki

Hmm, can't we do something like this instead:

---
 kernel/kthread.c |   10 ++++++++++
 1 file changed, 10 insertions(+)

Index: linux-2.6.21-rc7/kernel/kthread.c
===================================================================

kernel/kthread.c +1 -0
@@ -13,6 +13,7 @@
 #include <linux/file.h>
 #include <linux/module.h>
 #include <linux/mutex.h>
+#include <linux/freezer.h>
 #include <asm/semaphore.h>
 
 /*
@@ -232,6 +233,15 @@ int kthread_stop(struct task_struct *k)
 
Quoted 14 lines

kernel/kthread.c int kthread_stop(struct task_struct *k) · line 244

Quoted 11 lines
kernel/kthread.c +9 -0
 	/* Now set kthread_should_stop() to true, and wake it up. */
 	kthread_stop_info.k = k;
+	if (!(current->flags & PF_NOFREEZE)) {
+		/* If we are freezable, the freezer will wait for us */
+		task_lock(k);
+		k->flags |= PF_NOFREEZE;
+		if (frozen(k))
+			k->flags &= ~PF_FROZEN;
+
+		task_unlock(k);
+	}

Yes, we can do this for now since the tasks have only two freeze states, namely Freezeable and Non Freezeable.

No, we can't change k->flags, k owns its ->flags, and it is not atomic.

Rafael, may I suggest you to document task_lock() in thaw_process() ? This looks really confusing, as if task_lock() protects "p->flags &= ~PF_FROZEN".

Actually, task_lock() is needed to prevent the race with refrigerator() when the freezing fails, but this is not obvious.

Oleg.

#58 lore raw

Re: [RFC PATCH(experimental) 2/2] Fix freezer-kthread_stop race

Quoted 39 lines · Oleg Nesterov
Quoted 23 lines · Gautham R Shenoy
Quoted 21 lines · Rafael J. Wysocki

Hmm, can't we do something like this instead:

---
 kernel/kthread.c |   10 ++++++++++
 1 file changed, 10 insertions(+)

Index: linux-2.6.21-rc7/kernel/kthread.c
===================================================================

kernel/kthread.c +1 -0
@@ -13,6 +13,7 @@
 #include <linux/file.h>
 #include <linux/module.h>
 #include <linux/mutex.h>
+#include <linux/freezer.h>
 #include <asm/semaphore.h>
 
 /*
@@ -232,6 +233,15 @@ int kthread_stop(struct task_struct *k)
 
Quoted 14 lines

kernel/kthread.c int kthread_stop(struct task_struct *k) · line 244

Quoted 11 lines
kernel/kthread.c +9 -0
 	/* Now set kthread_should_stop() to true, and wake it up. */
 	kthread_stop_info.k = k;
+	if (!(current->flags & PF_NOFREEZE)) {
+		/* If we are freezable, the freezer will wait for us */
+		task_lock(k);
+		k->flags |= PF_NOFREEZE;
+		if (frozen(k))
+			k->flags &= ~PF_FROZEN;
+
+		task_unlock(k);
+	}

Yes, we can do this for now since the tasks have only two freeze states, namely Freezeable and Non Freezeable.

Quoted 1 line

No, we can't change k->flags, k owns its ->flags, and it is not atomic.

Yes, but if we move PF_FROZEN to a separate field in task_struct with appropriate locking, then it won't be a problem any more IMO.

Quoted 5 lines

Rafael, may I suggest you to document task_lock() in thaw_process() ? This looks really confusing, as if task_lock() protects "p->flags &= ~PF_FROZEN".

Actually, task_lock() is needed to prevent the race with refrigerator() when the freezing fails, but this is not obvious.

Sure, I will.

Greetings,
Rafael

#59 lore raw

Re: [RFC PATCH(experimental) 2/2] Fix freezer-kthread_stop race

In reply to Andrew Morton
Quoted 10 lines · Andrew Morton

On Thu, 19 Apr 2007 17:34:19 +0530
Gautham R Shenoy <[email protected]> wrote:

flush_workqueue() just needs to die. I think there are (almost) no legitimate users of it once cancel_work_sync() is merged.

Quoted 1 line

This patch attempts to address such a situation with a fix for kthread_stop.

Via wholly undescribed means :(

Sorry. I will document stuff better next time.

Quoted 8 lines
Quoted 2 lines

Strictly experimental. Compile tested on i386.

Rather than doing <whatever you did>, perhaps we could make the freezing process a dual-pass thing. On pass 1, mark all the threads as "we'll be freezing you soon" and on the second pass, do the actual freezing. Then, in problematic places such as kthread_stop() we can look to see if we'll soon be asked to freeze and if so, run try_to_freeze().

We can do that. Just that the freezer will now have to wait for 2 sets of ack's instead of 1 set before declaring the system as frozen.

But the whole point of the patch was so that a thread A can tell a thread B that it's dependent on the latter, and hence would like to postpone B's freezing for some time. So I am thinking if we can achieve this through the scheme you described.

Quoted 5 lines

Of course, running try_to_freeze() in kthread_stop() would be very wrong, so we'd actually need to do it in callers, preferably via a new kthread_stop_freezeable() wrapper.

Well, even then we'll need to ensure that a thread would not call kthread_stop_freezeable() with any locks held. That would mean more audits :)

Quoted 13 lines

And the two-pass-freeze thing is of course racy. It's also unnecessary:
setting a flag on every task in the machine is equivalent to setting a global variable. So perhaps just use a global variable?

int kthread_stop_freezeable(struct task_struct *k)

{
	if (freeze_state == ABOUT_TO_START) {
		wait_for(freeze_state == STARTED);
		try_to_freeze();
	}
	kthread_stop(k);
}
Quoted 3 lines

which is theoretically racy if another freeze_processes() starts immediately. Anyway - please have a think about it ;)

Sure, am already thinking about it :)

Quoted 7 lines

kernel/power/process.c line 34

Quoted 4 lines
kernel/power/process.c +4 -0
+static struct freezer_status_struct freezer_status = {
+				.lock = SPIN_LOCK_UNLOCKED,
+				.count = 0,
+			};

SPIN_LOCK_UNLOCKED is deprecated (it subverts lockdep)

Ok, will change it to __SPIN_LOCK_UNLOCKED(freezer_status.lock)

Quoted 32 lines
Quoted 19 lines

static inline int freezeable(struct task_struct * p) {

kernel/power/process.c +3 -1
 	if ((p == current) ||
@@ -45,7 +55,8 @@ void refrigerator(void)
 		 * *after* the freezer did the freezeable() check
 		 * on us.
 		 */
-		if (current->flags & PF_NOFREEZE) {
+		if ((current->flags & PF_NOFREEZE) ||
+		    test_tsk_thread_flag(current, TIF_FREEZER_HELD)) {
 			clear_tsk_thread_flag(current, TIF_FREEZE);
 			task_unlock(current);
 			return;
@@ -63,12 +74,16 @@ void refrigerator(void)
 	recalc_sigpending(); /* We sent fake signal, clean it up */
 	spin_unlock_irq(&current->sighand->siglock);
 
+	task_lock(current);
 	for (;;) {

kernel/power/process.c void refrigerator(void) · line 88

Quoted 10 lines
kernel/power/process.c +3 -0
 		set_current_state(TASK_UNINTERRUPTIBLE);
 		if (!frozen(current))
 			break;
+		task_unlock(current);
 		schedule();
+		task_lock(current);
 	}
+	task_unlock(current);
 	pr_debug("%s left refrigerator\n", current->comm);
 	current->state = save;

I guess we should use set_current_state() here.

Quoted 11 lines

kernel/power/process.c static void thaw_tasks(int thaw_user_spa · line 301

Quoted 8 lines
kernel/power/process.c +7 -0
+
+	if (thaw_user_space) {
+		spin_lock(&freezer_status.lock);
+			if (freezer_status.count < 0)
+				freezer_status.count++;
+		spin_unlock(&freezer_status.lock);
+	}
 }

whitespace went wrong

Huh! yeah, dunno how though.

Quoted 25 lines
Quoted 25 lines
include/asm-ia64/thread_info.h +4 -0
+#define TIF_FREEZER_HELD	21	/* is temporarily holding up the
+					 * process freezer
+					 */
 
 #define _TIF_SYSCALL_TRACE	(1 << TIF_SYSCALL_TRACE)
 #define _TIF_SYSCALL_AUDIT	(1 << TIF_SYSCALL_AUDIT)
@@ -102,6 +105,7 @@ struct thread_info {
 #define _TIF_MCA_INIT		(1 << TIF_MCA_INIT)
 #define _TIF_DB_DISABLED	(1 << TIF_DB_DISABLED)
 #define _TIF_FREEZE		(1 << TIF_FREEZE)
+#define _TIF_FREEZER_HELD	(1 << TIF_FREEZER_HELD)
 
 /* "work to do on user-return" bits */
 #define TIF_ALLWORK_MASK	(_TIF_NOTIFY_RESUME|_TIF_SIGPENDING|_TIF_NEED_RESCHED|_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT)

Index: linux-2.6.21-rc6/include/asm-mips/thread_info.h
===================================================================

include/asm-mips/thread_info.h +1 -0
@@ -120,6 +120,7 @@ register struct thread_info *__current_t
 #define TIF_MEMDIE		18
 #define TIF_FREEZE		19
 #define TIF_ALLOW_FP_IN_KERNEL	20
+#define TIF_FREEZER_HELD	21
 #define TIF_SYSCALL_TRACE	31	/* syscall trace active */
 
Quoted 13 lines

include/asm-mips/thread_info.h register struct thread_info *__current_t · line 136

Quoted 6 lines
include/asm-mips/thread_info.h +1 -0
 #define _TIF_SYSCALL_TRACE	(1<<TIF_SYSCALL_TRACE)
@@ -132,6 +133,7 @@ register struct thread_info *__current_t
 #define _TIF_USEDFPU		(1<<TIF_USEDFPU)
 #define _TIF_POLLING_NRFLAG	(1<<TIF_POLLING_NRFLAG)
 #define _TIF_FREEZE		(1<<TIF_FREEZE)
+#define _TIF_FREEZER_HELD	(1<<TIF_FREEZER_HELD)

hm, all this duplication is unpleasing. We could do something similar to include/linux/buffer_head.h:BH_PrivateStart here: get all architectures to define a TIF_COMMON_STUFF_STARTS_HERE then include asm-generic/whatever.h which defines all the flags which every architecture must define, as TIF_COMMON_STUFF_STARTS_HERE+0, TIF_COMMON_STUFF_STARTS_HERE+1, etc.

Ok.

Thanks and Regards
gautham.

Signature · 5 lines
Gautham R Shenoy
Linux Technology Center
IBM India.
"Freedom comes with a price tag of responsibility, which is still a bargain,
because Freedom is priceless!"
#60 lore raw

Re: [RFC PATCH(experimental) 2/2] Fix freezer-kthread_stop race

Hi!

Quoted 7 lines
Quoted 4 lines

Of course, running try_to_freeze() in kthread_stop() would be very wrong, so we'd actually need to do it in callers, preferably via a new kthread_stop_freezeable() wrapper.

Well, even then we'll need to ensure that a thread would not call kthread_stop_freezeable() with any locks held. That would mean more audits :)

I'll take audits over 200 new lines of code ;-). (And it should be reasonably simple to audit; person doing kthread_stop() -> ktrhread_stop_freezeable() should look after locks).
Pavel

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

Re: [RFC PATCH(experimental) 2/2] Fix freezer-kthread_stop race

In reply to Gautham R Shenoy
Quoted 7 lines · Gautham R Shenoy
kernel/power/process.c +1 -0
@@ -63,12 +74,16 @@ void refrigerator(void)
 	recalc_sigpending(); /* We sent fake signal, clean it up */
 	spin_unlock_irq(&current->sighand->siglock);
 
+	task_lock(current);
 	for (;;) {

kernel/power/process.c void refrigerator(void) · line 88

Quoted 10 lines
kernel/power/process.c +3 -0
 		set_current_state(TASK_UNINTERRUPTIBLE);
 		if (!frozen(current))
 			break;
+		task_unlock(current);
 		schedule();
+		task_lock(current);
 	}
+	task_unlock(current);
 	pr_debug("%s left refrigerator\n", current->comm);
 	current->state = save;

Just curious, why this change?

Quoted 5 lines
kernel/power/process.c +5 -0
+int hold_freezer_for_task(struct task_struct *p)
+{
+	int ret = 0;
+	spin_lock(&freezer_status.lock);
+	if (freezer_status.count >= 0)

kernel/power/process.c static inline int is_user_space(struct t · line 156

Quoted 10 lines
kernel/power/process.c +10 -0
+	{
+		set_tsk_thread_flag(p, TIF_FREEZER_HELD);
+		thaw_process(p);
+		freezer_status.count++;
+		ret = 1;
+	}
+	spin_unlock(&freezer_status.lock);
+
+	return ret;
+}

I think this can work if it is used only in kthread_stop(). But what if another task wants to do hold_freezer_for_task(p) ? freezer_status.count is recursive, but TIF_FREEZER_HELD is not. IOW, I believe this is not generic enough.

Also, you are planning to add different freezing states (FE_HOTPLUG_CPU, FE_SUSPEND, etc). In that case each of them needs a separate .count, because it should be negative when try_to_freeze_tasks() returns. Now consider the case when we are doing freeze_processes(FE_A | FE_B) ...

Oleg.

#62 lore raw

Re: [RFC PATCH(experimental) 2/2] Fix freezer-kthread_stop race

Quoted 6 lines · Oleg Nesterov
Quoted 5 lines · Gautham R Shenoy
kernel/power/process.c +0 -0
@@ -63,12 +74,16 @@ void refrigerator(void)
 	recalc_sigpending(); /* We sent fake signal, clean it up */
 	spin_unlock_irq(&current->sighand->siglock);
 
Quoted 14 lines

kernel/power/process.c void refrigerator(void) · line 88

Quoted 12 lines
kernel/power/process.c +4 -0
+	task_lock(current);
 	for (;;) {
 		set_current_state(TASK_UNINTERRUPTIBLE);
 		if (!frozen(current))
 			break;
+		task_unlock(current);
 		schedule();
+		task_lock(current);
 	}
+	task_unlock(current);
 	pr_debug("%s left refrigerator\n", current->comm);
 	current->state = save;

Just curious, why this change?

This can race with hold_freezer_for_task() calling thaw_process. Earlier thaw_process(p) was called only after the process 'p' was frozen. Now with hold_freezer_for_task(), we can as well call thaw_process(p) when 'p' is in the freezing stage. Hence the task_lock.
I know it's ugly, but couldn't think of any other alternative at that time.

Quoted 13 lines
Quoted 13 lines
kernel/power/process.c +12 -0
+int hold_freezer_for_task(struct task_struct *p)
+{
+	int ret = 0;
+	spin_lock(&freezer_status.lock);
+	if (freezer_status.count >= 0)
+	{
+		set_tsk_thread_flag(p, TIF_FREEZER_HELD);
+		thaw_process(p);
+		freezer_status.count++;
+		ret = 1;
+	}
+	spin_unlock(&freezer_status.lock);
Quoted 8 lines

kernel/power/process.c static inline int is_user_space(struct t · line 156

Quoted 3 lines
kernel/power/process.c +3 -0
+
+	return ret;
+}

I think this can work if it is used only in kthread_stop(). But what if another task wants to do hold_freezer_for_task(p) ? freezer_status.count is recursive, but TIF_FREEZER_HELD is not. IOW, I believe this is not generic enough.

Yes. If more than one tasks want another task to be temporarily thawed, this won't work. I hadn't anticipated such a case.

Quoted 5 lines

Also, you are planning to add different freezing states (FE_HOTPLUG_CPU, FE_SUSPEND, etc). In that case each of them needs a separate .count, because it should be negative when try_to_freeze_tasks() returns. Now consider the case when we are doing freeze_processes(FE_A | FE_B) ...

So can't we in that case find out the weight of the freeze_event variable and subtract that weight from the count (if the count is <=0 ) ?

Quoted 3 lines

Oleg.

Thanks for the review.
Regards
gautham.

Signature · 5 lines
Gautham R Shenoy
Linux Technology Center
IBM India.
"Freedom comes with a price tag of responsibility, which is still a bargain,
because Freedom is priceless!"
#63 lore raw

Re: [RFC PATCH(experimental) 2/2] Fix freezer-kthread_stop race

Quoted 23 lines · Gautham R Shenoy
Quoted 6 lines · Oleg Nesterov
Quoted 5 lines · Gautham R Shenoy
kernel/power/process.c +0 -0
@@ -63,12 +74,16 @@ void refrigerator(void)
 	recalc_sigpending(); /* We sent fake signal, clean it up */
 	spin_unlock_irq(&current->sighand->siglock);
 
Quoted 14 lines

kernel/power/process.c void refrigerator(void) · line 88

Quoted 12 lines
kernel/power/process.c +4 -0
+	task_lock(current);
 	for (;;) {
 		set_current_state(TASK_UNINTERRUPTIBLE);
 		if (!frozen(current))
 			break;
+		task_unlock(current);
 		schedule();
+		task_lock(current);
 	}
+	task_unlock(current);
 	pr_debug("%s left refrigerator\n", current->comm);
 	current->state = save;

Just curious, why this change?

Quoted 4 lines

This can race with hold_freezer_for_task() calling thaw_process. Earlier thaw_process(p) was called only after the process 'p' was frozen. Now with hold_freezer_for_task(), we can as well call thaw_process(p) when 'p' is in the freezing stage. Hence the task_lock.

hold_freezer_for_task()->thaw_process(p) will wake up the task. Or the caller of refrigerator will notice "!frozen()". Note that refrigerator() sets PF_FROZEN under task_lock().

In fact we have the same issue when thaw_tasks()->thaw_process(p) happens when the freezing fails. In that case 'p' may be not frozen.

Quoted 7 lines
Quoted 4 lines

Also, you are planning to add different freezing states (FE_HOTPLUG_CPU, FE_SUSPEND, etc). In that case each of them needs a separate .count, because it should be negative when try_to_freeze_tasks() returns. Now consider the case when we are doing freeze_processes(FE_A | FE_B) ...

So can't we in that case find out the weight of the freeze_event variable and subtract that weight from the count (if the count is <=0 ) ?

Probably yes... but if we are speaking about kthrad_stop() only, this could be afaics solved in more simple way, as Rafael suggests.

Oleg.

#64 lore raw

Re: [RFC PATCH(experimental) 2/2] Fix freezer-kthread_stop race

Quoted 29 lines · Oleg Nesterov
Quoted 23 lines · Gautham R Shenoy
Quoted 6 lines · Oleg Nesterov
Quoted 5 lines · Gautham R Shenoy
kernel/power/process.c +0 -0
@@ -63,12 +74,16 @@ void refrigerator(void)
 	recalc_sigpending(); /* We sent fake signal, clean it up */
 	spin_unlock_irq(&current->sighand->siglock);
 
Quoted 14 lines

kernel/power/process.c void refrigerator(void) · line 88

Quoted 12 lines
kernel/power/process.c +4 -0
+	task_lock(current);
 	for (;;) {
 		set_current_state(TASK_UNINTERRUPTIBLE);
 		if (!frozen(current))
 			break;
+		task_unlock(current);
 		schedule();
+		task_lock(current);
 	}
+	task_unlock(current);
 	pr_debug("%s left refrigerator\n", current->comm);
 	current->state = save;

Just curious, why this change?

Quoted 4 lines

This can race with hold_freezer_for_task() calling thaw_process. Earlier thaw_process(p) was called only after the process 'p' was frozen. Now with hold_freezer_for_task(), we can as well call thaw_process(p) when 'p' is in the freezing stage. Hence the task_lock.

Quoted 6 lines

hold_freezer_for_task()->thaw_process(p) will wake up the task. Or the caller of refrigerator will notice "!frozen()". Note that refrigerator() sets PF_FROZEN under task_lock().

In fact we have the same issue when thaw_tasks()->thaw_process(p) happens when the freezing fails. In that case 'p' may be not frozen.

Yes. I guess I was being too cautious :)

Quoted 11 lines
Quoted 8 lines
Quoted 5 lines

Also, you are planning to add different freezing states (FE_HOTPLUG_CPU, FE_SUSPEND, etc). In that case each of them needs a separate .count, because it should be negative when try_to_freeze_tasks() returns. Now consider the case when we are doing freeze_processes(FE_A | FE_B) ...

So can't we in that case find out the weight of the freeze_event variable and subtract that weight from the count (if the count is <=0 ) ?

Probably yes... but if we are speaking about kthrad_stop() only, this could be afaics solved in more simple way, as Rafael suggests.

I agree on this one. kthread_stop appears to be the only valid place where such a correction is required. So Rafael's approach makes sense here.

Quoted 3 lines

Oleg.

Thanks and Regards
gautham.

Signature · 5 lines
Gautham R Shenoy
Linux Technology Center
IBM India.
"Freedom comes with a price tag of responsibility, which is still a bargain,
because Freedom is priceless!"