Re: [PATCH 0/6] vfs: provide automatic kernel freeze / resume

From: Miklos Szeredi
Date: Tue Jun 06 2023 - 10:39:43 EST


On Sun, 14 May 2023 at 00:04, Askar Safin <safinaskar@xxxxxxxxx> wrote:
>
> Will this patch fix a long-standing fuse vs suspend bug? (
> https://bugzilla.kernel.org/show_bug.cgi?id=34932 )

No.

The solution to the fuse issue is to freeze processes that initiate
fuse requests *before* freezing processes that serve fuse requests.

The problem is finding out which is which. This can be complicated by
the fact that a process could be both serving requests *and*
initiating them (even without knowing).

The best idea so far is to let fuse servers set a process flag
(PF_FREEZE_LATE) that is inherited across fork/clone. For example the
sshfs server would do the following before starting request processing
or starting ssh:

echo 1 > /proc/self/freeze_late

This would make the sshfs and ssh processes be frozen after processes
that call into the sshfs mount.

After normal (non-server) processes are frozen, server processes
should not be getting new requests and can be frozen.

Issues remaining:

- if requests are stuck (e.g. network is down) then the requester
process can't be frozen and suspend will still fail.

- if server process is generating filesystem activity (new fuse
requests) spontaneously, then there's nothing to differentiate between
server processes and we are back to the original problem

Solution to both these are probably non-kernel: impacted servers need
to receive notification from systemd when suspend is starting and act
accordingly.

Attaching work-in-progress patch. This needs to be improved to freeze
server processes in a separate phase from kernel threads, but it
should be able to demonstrate the idea.

Thanks,
Miklos
From: Li Fei <fei.li@intel.com>
Subject: freezer: configure user space process frozen along with kernel threads
Date: Wed, 20 Feb 2013 10:15:25 +0800

There is well known issue that freezing will fail in case that fuse
daemon is frozen firstly with some requests not handled, as the fuse
usage task is waiting for the response from fuse daemon and can't be
frozen. To solve the issue as above, make fuse daemon frozen after
all user space processes frozen and during the kernel threads frozen
phase.

After discussion, at present it's generally agreed that:
1) It's only the fuse daemon itself know definitely that it needs
and can be frozen together with kernel threads;
2) It's helpful to expose interface that user space processes can
use to configure user space processes to be frozen together with
kernel threads.
More information can be found on https://lkml.org/lkml/2013/2/18/174.

To support the requirement above, attribute /proc/<PID>/freeze_late
is added, writing 1 to it will make the process to be frozen together
with kernel threads, and writing 0 to it will make the process to be
frozen together with user space processes.

Signed-off-by: Liu Chuansheng <chuansheng.liu@intel.com>
Signed-off-by: Wang Biao <biao.wang@intel.com>
Signed-off-by: Li Fei <fei.li@intel.com>
---
fs/proc/base.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++++
include/linux/freezer.h | 6 +++
include/linux/sched.h | 2 -
kernel/freezer.c | 29 ++++++++++++++++++
kernel/power/process.c | 2 -
5 files changed, 110 insertions(+), 3 deletions(-)

--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -98,6 +98,9 @@
#include <linux/cn_proc.h>
#include <linux/ksm.h>
#include <trace/events/oom.h>
+#ifdef CONFIG_FREEZER
+#include <linux/freezer.h>
+#endif
#include "internal.h"
#include "fd.h"

@@ -2252,6 +2255,70 @@ proc_map_files_get_link(struct dentry *d
return proc_pid_get_link(dentry, inode, done);
}

+#ifdef CONFIG_FREEZER
+
+static ssize_t freeze_late_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct task_struct *task = get_proc_task(file->f_path.dentry->d_inode);
+ char buffer[PROC_NUMBUF];
+ int freeze_late;
+ size_t len;
+ if (!task)
+ return -ESRCH;
+ freeze_late = (task->flags & PF_FREEZE_LATE) ? 1 : 0;
+ len = snprintf(buffer, sizeof(buffer), "%d\n", freeze_late);
+ return simple_read_from_buffer(buf, count, ppos, buffer, len);
+}
+
+static ssize_t freeze_late_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct task_struct *task;
+ char buffer[PROC_NUMBUF];
+ int freeze_late;
+ int err;
+
+ memset(buffer, 0, sizeof(buffer));
+ if (count > sizeof(buffer) - 1)
+ count = sizeof(buffer) - 1;
+ if (copy_from_user(buffer, buf, count)) {
+ err = -EFAULT;
+ goto out;
+ }
+
+ err = kstrtoint(strstrip(buffer), 0, &freeze_late);
+ if (err)
+ goto out;
+ if (freeze_late < FREEZE_LATE_MIN ||
+ freeze_late > FREEZE_LATE_MAX) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ task = get_proc_task(file->f_path.dentry->d_inode);
+ if (!task) {
+ err = -ESRCH;
+ goto out;
+ }
+
+ if (freeze_late)
+ set_freeze_late_flag(task);
+ else
+ clear_freeze_late_flag(task);
+
+out:
+ return err < 0 ? err : count;
+}
+
+static const struct file_operations proc_freeze_late_operations = {
+ .read = freeze_late_read,
+ .write = freeze_late_write,
+ .llseek = generic_file_llseek,
+};
+
+#endif
+
/*
* Identical to proc_pid_link_inode_operations except for get_link()
*/
@@ -3351,6 +3418,9 @@ static const struct pid_entry tgid_base_
ONE("ksm_merging_pages", S_IRUSR, proc_pid_ksm_merging_pages),
ONE("ksm_stat", S_IRUSR, proc_pid_ksm_stat),
#endif
+#ifdef CONFIG_FREEZER
+ REG("freeze_late", S_IRUGO|S_IWUSR, proc_freeze_late_operations),
+#endif
};

static int proc_tgid_base_readdir(struct file *file, struct dir_context *ctx)
@@ -3689,6 +3759,10 @@ static const struct pid_entry tid_base_s
ONE("ksm_merging_pages", S_IRUSR, proc_pid_ksm_merging_pages),
ONE("ksm_stat", S_IRUSR, proc_pid_ksm_stat),
#endif
+#ifdef CONFIG_FREEZER
+ REG("freeze_late", S_IRUGO|S_IWUSR, proc_freeze_late_operations),
+#endif
+
};

static int proc_tid_base_readdir(struct file *file, struct dir_context *ctx)
--- a/include/linux/freezer.h
+++ b/include/linux/freezer.h
@@ -60,6 +60,10 @@ static inline bool try_to_freeze(void)

extern bool freeze_task(struct task_struct *p);
extern bool set_freezable(void);
+#define FREEZE_LATE_MIN 0
+#define FREEZE_LATE_MAX 1
+extern void set_freeze_late_flag(struct task_struct *p);
+extern void clear_freeze_late_flag(struct task_struct *p);

#ifdef CONFIG_CGROUP_FREEZER
extern bool cgroup_freezing(struct task_struct *task);
@@ -84,6 +88,8 @@ static inline void thaw_kernel_threads(v
static inline bool try_to_freeze(void) { return false; }

static inline void set_freezable(void) {}
+static inline void set_freeze_late_flag(void) {}
+static inline void clear_freeze_late_flag(void) {}

#endif /* !CONFIG_FREEZER */

--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1737,7 +1737,7 @@ extern struct pid *cad_pid;
#define PF_USED_MATH 0x00002000 /* If unset the fpu must be initialized before use */
#define PF_USER_WORKER 0x00004000 /* Kernel thread cloned from userspace thread */
#define PF_NOFREEZE 0x00008000 /* This thread should not be frozen */
-#define PF__HOLE__00010000 0x00010000
+#define PF_FREEZE_LATE 0x00010000 /* Threads to be frozen along with kernel threads */
#define PF_KSWAPD 0x00020000 /* I am kswapd */
#define PF_MEMALLOC_NOFS 0x00040000 /* All allocation requests will inherit GFP_NOFS */
#define PF_MEMALLOC_NOIO 0x00080000 /* All allocation requests will inherit GFP_NOIO */
--- a/kernel/freezer.c
+++ b/kernel/freezer.c
@@ -46,7 +46,8 @@ bool freezing_slow_path(struct task_stru
if (pm_nosig_freezing || cgroup_freezing(p))
return true;

- if (pm_freezing && !(p->flags & PF_KTHREAD))
+ if (pm_freezing && !(p->flags & PF_KTHREAD) &&
+ !(p->flags & PF_FREEZE_LATE))
return true;

return false;
@@ -233,3 +234,29 @@ bool set_freezable(void)
return try_to_freeze();
}
EXPORT_SYMBOL(set_freezable);
+
+/**
+ * set_freeze_late_flag - make %p to be frozen late
+ *
+ * Make %p to be frozen by freezer along with kernel threads
+ */
+void set_freeze_late_flag(struct task_struct *p)
+{
+ spin_lock_irq(&freezer_lock);
+ p->flags |= PF_FREEZE_LATE;
+ spin_unlock_irq(&freezer_lock);
+}
+EXPORT_SYMBOL(set_freeze_late_flag);
+
+/**
+ * clear_freeze_late_flag - make %p to be frozen early
+ *
+ * Make %p to be frozen by freezer along with user space processes
+ */
+void clear_freeze_late_flag(struct task_struct *p)
+{
+ spin_lock_irq(&freezer_lock);
+ p->flags &= ~PF_FREEZE_LATE;
+ spin_unlock_irq(&freezer_lock);
+}
+EXPORT_SYMBOL(clear_freeze_late_flag);
--- a/kernel/power/process.c
+++ b/kernel/power/process.c
@@ -225,7 +225,7 @@ void thaw_kernel_threads(void)

read_lock(&tasklist_lock);
for_each_process_thread(g, p) {
- if (p->flags & PF_KTHREAD)
+ if (p->flags & (PF_KTHREAD | PF_FREEZE_LATE))
__thaw_task(p);
}
read_unlock(&tasklist_lock);