浏览代码

jffs2: Fix page lock / f->sem deadlock

With this fix, all code paths should now be obtaining the page lock before
f->sem.

Reported-by: Szabó Tamás <sztomi89@gmail.com>
Tested-by: Thomas Betker <thomas.betker@rohde-schwarz.com>
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
Cc: stable@vger.kernel.org
David Woodhouse 9 年之前
父节点
当前提交
49e91e7079
共有 2 个文件被更改,包括 11 次插入11 次删除
  1. 1 4
      fs/jffs2/README.Locking
  2. 10 7
      fs/jffs2/gc.c

+ 1 - 4
fs/jffs2/README.Locking

@@ -2,10 +2,6 @@
 	JFFS2 LOCKING DOCUMENTATION
 	JFFS2 LOCKING DOCUMENTATION
 	---------------------------
 	---------------------------
 
 
-At least theoretically, JFFS2 does not require the Big Kernel Lock
-(BKL), which was always helpfully obtained for it by Linux 2.4 VFS
-code. It has its own locking, as described below.
-
 This document attempts to describe the existing locking rules for
 This document attempts to describe the existing locking rules for
 JFFS2. It is not expected to remain perfectly up to date, but ought to
 JFFS2. It is not expected to remain perfectly up to date, but ought to
 be fairly close.
 be fairly close.
@@ -69,6 +65,7 @@ Ordering constraints:
 	   any f->sem held.
 	   any f->sem held.
 	2. Never attempt to lock two file mutexes in one thread.
 	2. Never attempt to lock two file mutexes in one thread.
 	   No ordering rules have been made for doing so.
 	   No ordering rules have been made for doing so.
+	3. Never lock a page cache page with f->sem held.
 
 
 
 
 	erase_completion_lock spinlock
 	erase_completion_lock spinlock

+ 10 - 7
fs/jffs2/gc.c

@@ -1296,14 +1296,17 @@ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_era
 		BUG_ON(start > orig_start);
 		BUG_ON(start > orig_start);
 	}
 	}
 
 
-	/* First, use readpage() to read the appropriate page into the page cache */
-	/* Q: What happens if we actually try to GC the _same_ page for which commit_write()
-	 *    triggered garbage collection in the first place?
-	 * A: I _think_ it's OK. read_cache_page shouldn't deadlock, we'll write out the
-	 *    page OK. We'll actually write it out again in commit_write, which is a little
-	 *    suboptimal, but at least we're correct.
-	 */
+	/* The rules state that we must obtain the page lock *before* f->sem, so
+	 * drop f->sem temporarily. Since we also hold c->alloc_sem, nothing's
+	 * actually going to *change* so we're safe; we only allow reading.
+	 *
+	 * It is important to note that jffs2_write_begin() will ensure that its
+	 * page is marked Uptodate before allocating space. That means that if we
+	 * end up here trying to GC the *same* page that jffs2_write_begin() is
+	 * trying to write out, read_cache_page() will not deadlock. */
+	mutex_unlock(&f->sem);
 	pg_ptr = jffs2_gc_fetch_page(c, f, start, &pg);
 	pg_ptr = jffs2_gc_fetch_page(c, f, start, &pg);
+	mutex_lock(&f->sem);
 
 
 	if (IS_ERR(pg_ptr)) {
 	if (IS_ERR(pg_ptr)) {
 		pr_warn("read_cache_page() returned error: %ld\n",
 		pr_warn("read_cache_page() returned error: %ld\n",