2019-02-20 10:18:19

by Hou Tao

[permalink] [raw]
Subject: [PATCH 0/2] jffs2: fixes for file creation failed halfway

Hi,

There are the fixes for file creation which failed halfway, the first
one is used to reclaim flash spaces had been used by the inode, and
the second one fixes a BUG assertion in jffs2_do_read_inode().

These two problems can be reproduced by concurrently creating files
until no space is left, and then removing these files, and repeating.

Comments are welcome.

Hou

Hou Tao (2):
jffs2: reset pino_nlink to 0 when inode creation failed
jffs2: handle INO_STATE_CLEARING in jffs2_do_read_inode()

fs/jffs2/dir.c | 28 ++++++++++++++++++++++++----
fs/jffs2/readinode.c | 1 +
2 files changed, 25 insertions(+), 4 deletions(-)

--
2.16.2.dirty



2019-02-20 10:19:21

by Hou Tao

[permalink] [raw]
Subject: [PATCH 1/2] jffs2: reset pino_nlink to 0 when inode creation failed

So jffs2_do_clear_inode() could mark all flash nodes used by
the inode as obsolete and GC procedure will reclaim these
flash nodes, else these flash spaces will not be reclaimable
forever.

Cc: [email protected]
Signed-off-by: Hou Tao <[email protected]>
---
fs/jffs2/dir.c | 28 ++++++++++++++++++++++++----
1 file changed, 24 insertions(+), 4 deletions(-)

diff --git a/fs/jffs2/dir.c b/fs/jffs2/dir.c
index f20cff1194bb..e02f85e516cb 100644
--- a/fs/jffs2/dir.c
+++ b/fs/jffs2/dir.c
@@ -156,6 +156,26 @@ static int jffs2_readdir(struct file *file, struct dir_context *ctx)

/***********************************************************************/

+static void jffs2_iget_failed(struct jffs2_sb_info *c, struct inode *inode)
+{
+ struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+
+ /*
+ * Reset pino_nlink to zero, so jffs2_do_clear_inode() will mark
+ * all flash nodes used by the inode as obsolete and GC procedure
+ * will reclaim these flash nodes, else these flash spaces will be
+ * unreclaimable forever.
+ *
+ * Update pino_nlink under inocache_lock, because no proceses could
+ * get the inode due to I_NEW flag, and only GC procedure may try to
+ * read pino_nlink under inocache_lock.
+ */
+ spin_lock(&c->inocache_lock);
+ f->inocache->pino_nlink = 0;
+ spin_unlock(&c->inocache_lock);
+
+ iget_failed(inode);
+}

static int jffs2_create(struct inode *dir_i, struct dentry *dentry,
umode_t mode, bool excl)
@@ -213,7 +233,7 @@ static int jffs2_create(struct inode *dir_i, struct dentry *dentry,
return 0;

fail:
- iget_failed(inode);
+ jffs2_iget_failed(c, inode);
jffs2_free_raw_inode(ri);
return ret;
}
@@ -433,7 +453,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
return 0;

fail:
- iget_failed(inode);
+ jffs2_iget_failed(c, inode);
return ret;
}

@@ -577,7 +597,7 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, umode_t mode
return 0;

fail:
- iget_failed(inode);
+ jffs2_iget_failed(c, inode);
return ret;
}

@@ -748,7 +768,7 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, umode_t mode
return 0;

fail:
- iget_failed(inode);
+ jffs2_iget_failed(c, inode);
return ret;
}

--
2.16.2.dirty


2019-02-20 10:19:57

by Hou Tao

[permalink] [raw]
Subject: [PATCH 2/2] jffs2: handle INO_STATE_CLEARING in jffs2_do_read_inode()

For inode that fails to be created midway, GC procedure may
try to GC its dnode, and in the following case BUG() will be
triggered:

CPU 0 CPU 1
in jffs2_do_create() in jffs2_garbage_collect_pass()

jffs2_write_dnode succeed
// for dirent
jffs2_reserve_space fail

inum = ic->ino
nlink = ic->pino_nlink (> 0)

iget_failed
make_bad_inode
remove_inode_hash
iput
jffs2_evict_inode
jffs2_do_clear_inode
jffs2_set_inocache_state(INO_STATE_CLEARING)

jffs2_gc_fetch_inode
jffs2_iget
// a new inode is created because
// the old inode had been unhashed
iget_locked
jffs2_do_read_inode
jffs2_get_ino_cache
// assert BUG()
f->inocache->state = INO_STATE_CLEARING

Fix it by waiting for its state changes to INO_STATE_CHECKEDABSENT.

Fixes: 67e345d17ff8 ("[JFFS2] Prevent ino cache removal for inodes in use")
Cc: [email protected]
Signed-off-by: Hou Tao <[email protected]>
---
fs/jffs2/readinode.c | 1 +
1 file changed, 1 insertion(+)

diff --git a/fs/jffs2/readinode.c b/fs/jffs2/readinode.c
index 389ea53ea487..0bae0583106e 100644
--- a/fs/jffs2/readinode.c
+++ b/fs/jffs2/readinode.c
@@ -1328,6 +1328,7 @@ int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,

case INO_STATE_CHECKING:
case INO_STATE_GC:
+ case INO_STATE_CLEARING:
/* If it's in either of these states, we need
to wait for whoever's got it to finish and
put it back. */
--
2.16.2.dirty