master: add support for kernel version v4.7..v4.14 88/177388/1
authorVyacheslav Cherkashin <v.cherkashin@samsung.com>
Tue, 17 Apr 2018 09:33:12 +0000 (12:33 +0300)
committerVyacheslav Cherkashin <v.cherkashin@samsung.com>
Fri, 27 Apr 2018 11:57:24 +0000 (14:57 +0300)
This is a workaround for fix deadlock in a kernel debugfs v4.7..v4.14

Change-Id: Ib1ad88b205f82e9503e1ca21e915a7fa2536a95b
Signed-off-by: Vyacheslav Cherkashin <v.cherkashin@samsung.com>
modules/master/swap_debugfs.c

index f437016..7c23752 100644 (file)
@@ -296,16 +296,6 @@ static ssize_t write_enable(struct file *file, const char __user *user_buf,
        char buf[32];
        size_t buf_size;
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0) && LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)
-       /*
-        * Global rcu-lock(debugfs_srcu) was added to debugfs
-        * in commit to linux-kernel:9fd4dcece43.
-        * It causes deadlock in debugfs_remove_recursive().
-        */
-       pr_err("This kernel version does not supported\n");
-       return -EPERM;
-#endif
-
        buf_size = min(count, (sizeof(buf) - 1));
        if (copy_from_user(buf, user_buf, buf_size))
                return -EFAULT;
@@ -315,7 +305,101 @@ static ssize_t write_enable(struct file *file, const char __user *user_buf,
        return ret ? ret : count;
 }
 
+
+/* For kernel v4.7..v4.14 */
+#define WORKAROUND_DEADLOCK_IN_DEBUGFS ( \
+       LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0) && \
+       LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0) \
+)
+
+#if WORKAROUND_DEADLOCK_IN_DEBUGFS
+/*
+ * Global rcu-lock(debugfs_srcu) was added to kernel debugfs
+ * in commit 9fd4dcece43 and removed in commit c9afbec2708.
+ * In case write 0 to "/sys/kernel/debug/swap/enable"
+ * it causes deadlock in debugfs_remove_recursive().
+ *
+ *   This workaround solves this problem!
+ */
+
+
+#include <linux/srcu.h>
+
+
+DEFINE_STATIC_SRCU(g_workaround_srcu);
+
+static ssize_t do_proxy_write(struct file *file, const char __user *user_buf,
+                             size_t count, loff_t *ppos)
+{
+       ssize_t ret;
+       int srcu_idx;
+       struct file_operations *real_fops = file->f_path.dentry->d_fsdata;
+
+       /* use file start */
+       srcu_idx = srcu_read_lock(&g_workaround_srcu);
+       barrier();
+
+       ret = real_fops->write(file, user_buf, count, ppos);
+
+       /* use file finish */
+       srcu_read_unlock(&g_workaround_srcu, srcu_idx);
+
+       return ret;
+}
+
+static void *g_real_proxy_write;
+
+static int workaround_proxy_enable_open(struct inode *inode, struct file *filp)
+{
+       struct file_operations *fops;
+
+       /* remove const mode */
+       fops = (struct file_operations *)filp->f_op;
+
+       if (g_real_proxy_write) {
+               BUG_ON(g_real_proxy_write != fops->write);
+       } else {
+               /* save write method */
+               g_real_proxy_write = fops->write;
+       }
+
+       /* replace write method */
+       fops->write = do_proxy_write;
+
+       return 0;
+}
+
+static int workaround_proxy_enable_release(struct inode *inode,
+                                          struct file *filp)
+{
+       struct file_operations *fops;
+       BUG_ON(!g_real_proxy_write);
+
+       /* remove const mode */
+       fops = (struct file_operations *)filp->f_op;
+
+       /* restore write method */
+       fops->write = g_real_proxy_write;
+
+       return 0;
+}
+
+static void fops_enable_sync(void)
+{
+       synchronize_srcu(&g_workaround_srcu);
+}
+#else /* WORKAROUND_DEADLOCK_IN_DEBUGFS */
+static void fops_enable_sync(void)
+{
+}
+#endif /* WORKAROUND_DEADLOCK_IN_DEBUGFS */
+
+
 static const struct file_operations fops_enable = {
+#if WORKAROUND_DEADLOCK_IN_DEBUGFS
+       .open = workaround_proxy_enable_open,
+       .release = workaround_proxy_enable_release,
+#endif /* WORKAROUND_DEADLOCK_IN_DEBUGFS */
        .owner = THIS_MODULE,
        .read = read_enable,
        .write = write_enable,
@@ -351,6 +435,8 @@ static void debugfs_dir_exit(void)
 
        swap_dir = NULL;
        debugfs_remove_recursive(dir);
+
+       fops_enable_sync();
 }
 
 /**