[Yaffs] [PATCH] yaffs: fix softlockup cauesed by inode delet…

Top Page
Attachments:
Message as email
+ (text/plain)
+ 0001-yaffs-fix-softlockup-cauesed-by-inode-deleted-when-s.patch (text/x-diff)
Delete this message
Reply to this message
Author: JiSheng Zhang
Date:  
To: yaffs
Subject: [Yaffs] [PATCH] yaffs: fix softlockup cauesed by inode deleted when scanning s_inodes list
Hi List,

I can only send email using webui now. so the patch is attached. I can
resend the patch in email body if necessary later.

The bug can only be triggered under SMP and heavy stress test.

What happen is that the list_for_each_entry() loop in yaffs flush inodes
routine can race with inodes deleting, so the inode can go away before
list_for_each_entry() get the next node. This leads to a endless loop
which will cause softlockup. We fix the problem by keeping reference to
it and putting the reference only after we have safely resumed the scan
of the inode list.

Since __iget() and the inode lock are not exported, yaffs can not be selected
as M after this change

Signed-off-by: Jisheng Zhang <>
---
Kconfig_multi      |    2 +-
Kconfig_single     |    2 +-
yaffs_vfs_multi.c  |   29 ++++++++++++++++++++++++++++-
yaffs_vfs_single.c |   24 +++++++++++++++++++++++-
4 files changed, 53 insertions(+), 4 deletions(-)
From 2b23fa0af1fcefe80c32389733364e0813edec92 Mon Sep 17 00:00:00 2001
From: Jisheng Zhang <>
Date: Wed, 31 Aug 2011 14:58:42 +0800
Subject: [PATCH] yaffs: fix softlockup cauesed by inode deleted when scanning
s_inodes list

What happen is that the list_for_each_entry() loop in yaffs flush inodes
routine can race with inodes deleting, so the inode can go away before
list_for_each_entry() get the next node. This leads to a endless loop
which will cause softlockup. We fix the problem by keeping reference to
it and putting the reference only after we have safely resumed the scan
of the inode list.

Since __iget() and the inode lock are not exported, yaffs can not be selected
as M after this change

Signed-off-by: Jisheng Zhang <>
---
 Kconfig_multi      |    2 +-
 Kconfig_single     |    2 +-
 yaffs_vfs_multi.c  |   29 ++++++++++++++++++++++++++++-
 yaffs_vfs_single.c |   24 +++++++++++++++++++++++-
 4 files changed, 53 insertions(+), 4 deletions(-)


diff --git a/Kconfig_multi b/Kconfig_multi
index 658feea..bd807b3 100644
--- a/Kconfig_multi
+++ b/Kconfig_multi
@@ -3,7 +3,7 @@
#

 config YAFFS_FS
-    tristate "yaffs2 file system support"
+    bool "yaffs2 file system support"
     default n
     depends on MTD_BLOCK
     select YAFFS_YAFFS1
diff --git a/Kconfig_single b/Kconfig_single
index bd6e8df..b7519db 100644
--- a/Kconfig_single
+++ b/Kconfig_single
@@ -3,7 +3,7 @@
 #


 config YAFFS_FS
-    tristate "yaffs2 file system support"
+    bool "yaffs2 file system support"
     default n
     depends on MTD_BLOCK
     select YAFFS_YAFFS1
diff --git a/yaffs_vfs_multi.c b/yaffs_vfs_multi.c
index b8e5124..5d4ec85 100644
--- a/yaffs_vfs_multi.c
+++ b/yaffs_vfs_multi.c
@@ -2136,20 +2136,47 @@ static int yaffs_statfs(struct super_block *sb, struct statfs *buf)
     return 0;
 }


+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39))
+#define INODE_LOCK    inode_sb_list_lock
+#else
+#define INODE_LOCK    inode_lock
+#endif
 static void yaffs_flush_inodes(struct super_block *sb)
 {
-    struct inode *iptr;
+    struct inode *iptr, *old_iptr = NULL;
     struct yaffs_obj *obj;
+    struct yaffs_dev *dev = yaffs_super_to_dev(sb);
+    extern spinlock_t INODE_LOCK;


+    spin_lock(&INODE_LOCK);
     list_for_each_entry(iptr, &sb->s_inodes, i_sb_list) {
         obj = yaffs_inode_to_obj(iptr);
+        __iget(iptr);
+        spin_unlock(&INODE_LOCK);
+
         if (obj) {
             yaffs_trace(YAFFS_TRACE_OS,
                 "flushing obj %d",
                 obj->obj_id);
             yaffs_flush_file(obj, 1, 0);
         }
+
+        /* We hold a reference to 'iptr' so it couldn't have been
+         * removed from s_inodes list while we dropped the inode lock.
+         * We cannot iput the iptr now as we can be holding the last
+         * reference and we cannot iput it under inode lock. So we
+         * keep the reference and iput it later. And iput may call
+         * yaffs_delete_inode which will lock the yaffs gross lock
+         * so we should unlock it firstly and lock again after iput.
+         */
+        yaffs_gross_unlock(dev);
+        iput(old_iptr);
+        yaffs_gross_lock(dev);
+        old_iptr = iptr;
+        spin_lock(&INODE_LOCK);
     }
+    spin_unlock(&INODE_LOCK);
+    iput(old_iptr);
 }


static void yaffs_flush_super(struct super_block *sb, int do_checkpoint)
diff --git a/yaffs_vfs_single.c b/yaffs_vfs_single.c
index f822845..bc9a599 100644
--- a/yaffs_vfs_single.c
+++ b/yaffs_vfs_single.c
@@ -1481,17 +1481,39 @@ static int yaffs_statfs(struct dentry *dentry, struct kstatfs *buf)

 static void yaffs_flush_inodes(struct super_block *sb)
 {
-    struct inode *iptr;
+    struct inode *iptr, *old_iptr = NULL;
     struct yaffs_obj *obj;
+    struct yaffs_dev *dev = yaffs_super_to_dev(sb);
+    extern spinlock_t inode_sb_list_lock;


+    spin_lock(&inode_sb_list_lock);
     list_for_each_entry(iptr, &sb->s_inodes, i_sb_list) {
         obj = yaffs_inode_to_obj(iptr);
+        __iget(iptr);
+        spin_unlock(&inode_sb_list_lock);
+
         if (obj) {
             yaffs_trace(YAFFS_TRACE_OS,
                 "flushing obj %d", obj->obj_id);
             yaffs_flush_file(obj, 1, 0);
         }
+
+        /* We hold a reference to 'iptr' so it couldn't have been
+         * removed from s_inodes list while we dropped the inode lock.
+         * We cannot iput the iptr now as we can be holding the last
+         * reference and we cannot iput it under inode lock. So we
+         * keep the reference and iput it later. And iput may call
+         * yaffs_delete_inode which will lock the yaffs gross lock
+         * so we should unlock it firstly and lock again after iput.
+         */
+        yaffs_gross_unlock(dev);
+        iput(old_iptr);
+        yaffs_gross_lock(dev);
+        old_iptr = iptr;
+        spin_lock(&inode_sb_list_lock);
     }
+    spin_unlock(&inode_sb_list_lock);
+    iput(old_iptr);
 }


static void yaffs_flush_super(struct super_block *sb, int do_checkpoint)
--
1.7.5.4