[RFC][PATCH 2/2] Freezer: Try to handle killable tasks

From: Rafael J. Wysocki
Date: Tue May 06 2008 - 18:12:58 EST


From: Rafael J. Wysocki <rjw@xxxxxxx>

The introduction of TASK_KILLABLE allows the freezer to work in some situation
that it could not handle before.

Make the freezer handle killable tasks and add try_to_freeze() in some places
where it is safe to freeze a (killable) task. Introduce the
wait_event_killable_freezable() macro to be used wherever the freezing of
a waiting killable task is desirable.

Signed-off-by: Rafael J. Wysocki <rjw@xxxxxxx>
---
fs/nfs/inode.c | 2 ++
fs/nfs/nfs3proc.c | 2 ++
fs/nfs/nfs4proc.c | 4 ++++
fs/nfs/pagelist.c | 8 ++++++--
fs/smbfs/request.c | 2 ++
include/linux/freezer.h | 20 +++++++++++++++++---
kernel/mutex.c | 3 +++
kernel/power/process.c | 6 ++++--
kernel/sched.c | 2 ++
net/sunrpc/sched.c | 2 ++
10 files changed, 44 insertions(+), 7 deletions(-)

Index: linux-2.6/fs/nfs/nfs3proc.c
===================================================================
--- linux-2.6.orig/fs/nfs/nfs3proc.c
+++ linux-2.6/fs/nfs/nfs3proc.c
@@ -17,6 +17,7 @@
#include <linux/nfs_page.h>
#include <linux/lockd/bind.h>
#include <linux/nfs_mount.h>
+#include <linux/freezer.h>

#include "iostat.h"
#include "internal.h"
@@ -33,6 +34,7 @@ nfs3_rpc_wrapper(struct rpc_clnt *clnt,
if (res != -EJUKEBOX)
break;
schedule_timeout_killable(NFS_JUKEBOX_RETRY_TIME);
+ try_to_freeze();
res = -ERESTARTSYS;
} while (!fatal_signal_pending(current));
return res;
Index: linux-2.6/fs/nfs/nfs4proc.c
===================================================================
--- linux-2.6.orig/fs/nfs/nfs4proc.c
+++ linux-2.6/fs/nfs/nfs4proc.c
@@ -48,6 +48,7 @@
#include <linux/smp_lock.h>
#include <linux/namei.h>
#include <linux/mount.h>
+#include <linux/freezer.h>

#include "nfs4_fs.h"
#include "delegation.h"
@@ -2788,6 +2789,7 @@ static int nfs4_wait_bit_killable(void *
if (fatal_signal_pending(current))
return -ERESTARTSYS;
schedule();
+ try_to_freeze();
return 0;
}

@@ -2819,6 +2821,8 @@ static int nfs4_delay(struct rpc_clnt *c
schedule_timeout_killable(*timeout);
if (fatal_signal_pending(current))
res = -ERESTARTSYS;
+ else
+ try_to_freeze();
*timeout <<= 1;
return res;
}
Index: linux-2.6/fs/nfs/pagelist.c
===================================================================
--- linux-2.6.orig/fs/nfs/pagelist.c
+++ linux-2.6/fs/nfs/pagelist.c
@@ -18,6 +18,7 @@
#include <linux/nfs_page.h>
#include <linux/nfs_fs.h>
#include <linux/nfs_mount.h>
+#include <linux/freezer.h>

#include "internal.h"

@@ -68,6 +69,7 @@ nfs_create_request(struct nfs_open_conte

if (fatal_signal_pending(current))
return ERR_PTR(-ERESTARTSYS);
+ try_to_freeze();
yield();
}

@@ -180,10 +182,12 @@ static int nfs_wait_bit_killable(void *w
{
int ret = 0;

- if (fatal_signal_pending(current))
+ if (fatal_signal_pending(current)) {
ret = -ERESTARTSYS;
- else
+ } else {
schedule();
+ try_to_freeze();
+ }
return ret;
}

Index: linux-2.6/fs/smbfs/request.c
===================================================================
--- linux-2.6.orig/fs/smbfs/request.c
+++ linux-2.6/fs/smbfs/request.c
@@ -12,6 +12,7 @@
#include <linux/slab.h>
#include <linux/net.h>
#include <linux/sched.h>
+#include <linux/freezer.h>

#include <linux/smb_fs.h>
#include <linux/smbno.h>
@@ -109,6 +110,7 @@ struct smb_request *smb_alloc_request(st
return ERR_PTR(-ERESTARTSYS);
current->policy = SCHED_YIELD;
schedule();
+ try_to_freeze();
#else
/* FIXME: we want something like nfs does above, but that
requires changes to all callers and can wait. */
Index: linux-2.6/include/linux/freezer.h
===================================================================
--- linux-2.6.orig/include/linux/freezer.h
+++ linux-2.6/include/linux/freezer.h
@@ -137,8 +137,9 @@ static inline void set_freezable_with_si
}

/*
- * Freezer-friendly wrappers around wait_event_interruptible() and
- * wait_event_interruptible_timeout(), originally defined in <linux/wait.h>
+ * Freezer-friendly wrappers around wait_event_interruptible(),
+ * wait_event_interruptible_timeout(), wait_event_killable(),
+ * originally defined in <linux/wait.h>
*/

#define wait_event_freezable(wq, condition) \
@@ -155,7 +156,6 @@ static inline void set_freezable_with_si
__retval; \
})

-
#define wait_event_freezable_timeout(wq, condition, timeout) \
({ \
long __retval = timeout; \
@@ -166,6 +166,20 @@ static inline void set_freezable_with_si
} while (try_to_freeze()); \
__retval; \
})
+
+#define wait_event_killable_freezable(wq, condition) \
+({ \
+ int __retval; \
+ do { \
+ __retval = wait_event_killable(wq, \
+ (condition) || freezing(current)); \
+ if (__retval && !freezing(current)) \
+ break; \
+ else if (!(condition)) \
+ __retval = -ERESTARTSYS; \
+ } while (try_to_freeze()); \
+ __retval; \
+})
#else /* !CONFIG_PM_SLEEP */
static inline int frozen(struct task_struct *p) { return 0; }
static inline int freezing(struct task_struct *p) { return 0; }
Index: linux-2.6/kernel/mutex.c
===================================================================
--- linux-2.6.orig/kernel/mutex.c
+++ linux-2.6/kernel/mutex.c
@@ -18,6 +18,7 @@
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/debug_locks.h>
+#include <linux/freezer.h>

/*
* In the DEBUG case we are using the "NULL fastpath" for mutexes,
@@ -182,6 +183,8 @@ __mutex_lock_common(struct mutex *lock,
/* didnt get the lock, go to sleep: */
spin_unlock_mutex(&lock->wait_lock, flags);
schedule();
+ if (state == TASK_KILLABLE)
+ try_to_freeze();
spin_lock_mutex(&lock->wait_lock, flags);
}

Index: linux-2.6/kernel/sched.c
===================================================================
--- linux-2.6.orig/kernel/sched.c
+++ linux-2.6/kernel/sched.c
@@ -4769,6 +4769,8 @@ do_wait_for_common(struct completion *x,
__set_current_state(state);
spin_unlock_irq(&x->wait.lock);
timeout = schedule_timeout(timeout);
+ if (state == TASK_KILLABLE)
+ try_to_freeze();
spin_lock_irq(&x->wait.lock);
if (!timeout) {
__remove_wait_queue(&x->wait, &wait);
Index: linux-2.6/net/sunrpc/sched.c
===================================================================
--- linux-2.6.orig/net/sunrpc/sched.c
+++ linux-2.6/net/sunrpc/sched.c
@@ -19,6 +19,7 @@
#include <linux/smp_lock.h>
#include <linux/spinlock.h>
#include <linux/mutex.h>
+#include <linux/freezer.h>

#include <linux/sunrpc/clnt.h>

@@ -227,6 +228,7 @@ static int rpc_wait_bit_killable(void *w
if (fatal_signal_pending(current))
return -ERESTARTSYS;
schedule();
+ try_to_freeze();
return 0;
}

Index: linux-2.6/fs/nfs/inode.c
===================================================================
--- linux-2.6.orig/fs/nfs/inode.c
+++ linux-2.6/fs/nfs/inode.c
@@ -37,6 +37,7 @@
#include <linux/vfs.h>
#include <linux/inet.h>
#include <linux/nfs_xdr.h>
+#include <linux/freezer.h>

#include <asm/system.h>
#include <asm/uaccess.h>
@@ -426,6 +427,7 @@ static int nfs_wait_schedule(void *word)
if (signal_pending(current))
return -ERESTARTSYS;
schedule();
+ try_to_freeze();
return 0;
}

Index: linux-2.6/kernel/power/process.c
===================================================================
--- linux-2.6.orig/kernel/power/process.c
+++ linux-2.6/kernel/power/process.c
@@ -77,7 +77,9 @@ static void fake_signal_wake_up(struct t
unsigned long flags;

spin_lock_irqsave(&p->sighand->siglock, flags);
- signal_wake_up(p, 0);
+ set_tsk_thread_flag(p, TIF_SIGPENDING);
+ if (!wake_up_state(p, TASK_INTERRUPTIBLE | TASK_KILLABLE))
+ kick_process(p);
spin_unlock_irqrestore(&p->sighand->siglock, flags);
}

@@ -124,7 +126,7 @@ static bool freeze_task(struct task_stru
} else if (sig_only) {
return false;
} else {
- wake_up_state(p, TASK_INTERRUPTIBLE);
+ wake_up_state(p, TASK_INTERRUPTIBLE | TASK_KILLABLE);
}

return true;
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/