From: Greg Kroah-Hartman Date: Sat, 5 Mar 2016 19:37:02 +0000 (-0800) Subject: 3.10-stable patches X-Git-Tag: v3.10.100~15 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=6241889df82081c6d5340a02f4097f2dcde820ed;p=thirdparty%2Fkernel%2Fstable-queue.git 3.10-stable patches added patches: fix-directory-hardlinks-from-deleted-directories.patch jffs2-fix-page-lock-f-sem-deadlock.patch revert-jffs2-fix-lock-acquisition-order-bug-in-jffs2_write_begin.patch --- diff --git a/queue-3.10/fix-directory-hardlinks-from-deleted-directories.patch b/queue-3.10/fix-directory-hardlinks-from-deleted-directories.patch new file mode 100644 index 00000000000..6c9736e7f9c --- /dev/null +++ b/queue-3.10/fix-directory-hardlinks-from-deleted-directories.patch @@ -0,0 +1,193 @@ +From be629c62a603e5935f8177fd8a19e014100a259e Mon Sep 17 00:00:00 2001 +From: David Woodhouse +Date: Mon, 1 Feb 2016 14:04:46 +0000 +Subject: Fix directory hardlinks from deleted directories +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: David Woodhouse + +commit be629c62a603e5935f8177fd8a19e014100a259e upstream. + +When a directory is deleted, we don't take too much care about killing off +all the dirents that belong to it — on the basis that on remount, the scan +will conclude that the directory is dead anyway. + +This doesn't work though, when the deleted directory contained a child +directory which was moved *out*. In the early stages of the fs build +we can then end up with an apparent hard link, with the child directory +appearing both in its true location, and as a child of the original +directory which are this stage of the mount process we don't *yet* know +is defunct. + +To resolve this, take out the early special-casing of the "directories +shall not have hard links" rule in jffs2_build_inode_pass1(), and let the +normal nlink processing happen for directories as well as other inodes. + +Then later in the build process we can set ic->pino_nlink to the parent +inode#, as is required for directories during normal operaton, instead +of the nlink. And complain only *then* about hard links which are still +in evidence even after killing off all the unreachable paths. + +Reported-by: Liu Song +Signed-off-by: David Woodhouse +Signed-off-by: Greg Kroah-Hartman + +--- + fs/jffs2/build.c | 75 +++++++++++++++++++++++++++++++++++++++------------- + fs/jffs2/nodelist.h | 6 +++- + 2 files changed, 62 insertions(+), 19 deletions(-) + +--- a/fs/jffs2/build.c ++++ b/fs/jffs2/build.c +@@ -49,7 +49,8 @@ next_inode(int *i, struct jffs2_inode_ca + + + static void jffs2_build_inode_pass1(struct jffs2_sb_info *c, +- struct jffs2_inode_cache *ic) ++ struct jffs2_inode_cache *ic, ++ int *dir_hardlinks) + { + struct jffs2_full_dirent *fd; + +@@ -68,19 +69,21 @@ static void jffs2_build_inode_pass1(stru + dbg_fsbuild("child \"%s\" (ino #%u) of dir ino #%u doesn't exist!\n", + fd->name, fd->ino, ic->ino); + jffs2_mark_node_obsolete(c, fd->raw); ++ /* Clear the ic/raw union so it doesn't cause problems later. */ ++ fd->ic = NULL; + continue; + } + ++ /* From this point, fd->raw is no longer used so we can set fd->ic */ ++ fd->ic = child_ic; ++ child_ic->pino_nlink++; ++ /* If we appear (at this stage) to have hard-linked directories, ++ * set a flag to trigger a scan later */ + if (fd->type == DT_DIR) { +- if (child_ic->pino_nlink) { +- JFFS2_ERROR("child dir \"%s\" (ino #%u) of dir ino #%u appears to be a hard link\n", +- fd->name, fd->ino, ic->ino); +- /* TODO: What do we do about it? */ +- } else { +- child_ic->pino_nlink = ic->ino; +- } +- } else +- child_ic->pino_nlink++; ++ child_ic->flags |= INO_FLAGS_IS_DIR; ++ if (child_ic->pino_nlink > 1) ++ *dir_hardlinks = 1; ++ } + + dbg_fsbuild("increased nlink for child \"%s\" (ino #%u)\n", fd->name, fd->ino); + /* Can't free scan_dents so far. We might need them in pass 2 */ +@@ -94,8 +97,7 @@ static void jffs2_build_inode_pass1(stru + */ + static int jffs2_build_filesystem(struct jffs2_sb_info *c) + { +- int ret; +- int i; ++ int ret, i, dir_hardlinks = 0; + struct jffs2_inode_cache *ic; + struct jffs2_full_dirent *fd; + struct jffs2_full_dirent *dead_fds = NULL; +@@ -119,7 +121,7 @@ static int jffs2_build_filesystem(struct + /* Now scan the directory tree, increasing nlink according to every dirent found. */ + for_each_inode(i, c, ic) { + if (ic->scan_dents) { +- jffs2_build_inode_pass1(c, ic); ++ jffs2_build_inode_pass1(c, ic, &dir_hardlinks); + cond_resched(); + } + } +@@ -155,6 +157,20 @@ static int jffs2_build_filesystem(struct + } + + dbg_fsbuild("pass 2a complete\n"); ++ ++ if (dir_hardlinks) { ++ /* If we detected directory hardlinks earlier, *hopefully* ++ * they are gone now because some of the links were from ++ * dead directories which still had some old dirents lying ++ * around and not yet garbage-collected, but which have ++ * been discarded above. So clear the pino_nlink field ++ * in each directory, so that the final scan below can ++ * print appropriate warnings. */ ++ for_each_inode(i, c, ic) { ++ if (ic->flags & INO_FLAGS_IS_DIR) ++ ic->pino_nlink = 0; ++ } ++ } + dbg_fsbuild("freeing temporary data structures\n"); + + /* Finally, we can scan again and free the dirent structs */ +@@ -162,6 +178,33 @@ static int jffs2_build_filesystem(struct + while(ic->scan_dents) { + fd = ic->scan_dents; + ic->scan_dents = fd->next; ++ /* We do use the pino_nlink field to count nlink of ++ * directories during fs build, so set it to the ++ * parent ino# now. Now that there's hopefully only ++ * one. */ ++ if (fd->type == DT_DIR) { ++ if (!fd->ic) { ++ /* We'll have complained about it and marked the coresponding ++ raw node obsolete already. Just skip it. */ ++ continue; ++ } ++ ++ /* We *have* to have set this in jffs2_build_inode_pass1() */ ++ BUG_ON(!(fd->ic->flags & INO_FLAGS_IS_DIR)); ++ ++ /* We clear ic->pino_nlink ∀ directories' ic *only* if dir_hardlinks ++ * is set. Otherwise, we know this should never trigger anyway, so ++ * we don't do the check. And ic->pino_nlink still contains the nlink ++ * value (which is 1). */ ++ if (dir_hardlinks && fd->ic->pino_nlink) { ++ JFFS2_ERROR("child dir \"%s\" (ino #%u) of dir ino #%u is also hard linked from dir ino #%u\n", ++ fd->name, fd->ino, ic->ino, fd->ic->pino_nlink); ++ /* Should we unlink it from its previous parent? */ ++ } ++ ++ /* For directories, ic->pino_nlink holds that parent inode # */ ++ fd->ic->pino_nlink = ic->ino; ++ } + jffs2_free_full_dirent(fd); + } + ic->scan_dents = NULL; +@@ -240,11 +283,7 @@ static void jffs2_build_remove_unlinked_ + + /* Reduce nlink of the child. If it's now zero, stick it on the + dead_fds list to be cleaned up later. Else just free the fd */ +- +- if (fd->type == DT_DIR) +- child_ic->pino_nlink = 0; +- else +- child_ic->pino_nlink--; ++ child_ic->pino_nlink--; + + if (!child_ic->pino_nlink) { + dbg_fsbuild("inode #%u (\"%s\") now has no links; adding to dead_fds list.\n", +--- a/fs/jffs2/nodelist.h ++++ b/fs/jffs2/nodelist.h +@@ -194,6 +194,7 @@ struct jffs2_inode_cache { + #define INO_STATE_CLEARING 6 /* In clear_inode() */ + + #define INO_FLAGS_XATTR_CHECKED 0x01 /* has no duplicate xattr_ref */ ++#define INO_FLAGS_IS_DIR 0x02 /* is a directory */ + + #define RAWNODE_CLASS_INODE_CACHE 0 + #define RAWNODE_CLASS_XATTR_DATUM 1 +@@ -249,7 +250,10 @@ struct jffs2_readinode_info + + struct jffs2_full_dirent + { +- struct jffs2_raw_node_ref *raw; ++ union { ++ struct jffs2_raw_node_ref *raw; ++ struct jffs2_inode_cache *ic; /* Just during part of build */ ++ }; + struct jffs2_full_dirent *next; + uint32_t version; + uint32_t ino; /* == zero for unlink */ diff --git a/queue-3.10/jffs2-fix-page-lock-f-sem-deadlock.patch b/queue-3.10/jffs2-fix-page-lock-f-sem-deadlock.patch new file mode 100644 index 00000000000..7bda632f175 --- /dev/null +++ b/queue-3.10/jffs2-fix-page-lock-f-sem-deadlock.patch @@ -0,0 +1,73 @@ +From 49e91e7079febe59a20ca885a87dd1c54240d0f1 Mon Sep 17 00:00:00 2001 +From: David Woodhouse +Date: Mon, 1 Feb 2016 12:37:20 +0000 +Subject: jffs2: Fix page lock / f->sem deadlock +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: David Woodhouse + +commit 49e91e7079febe59a20ca885a87dd1c54240d0f1 upstream. + +With this fix, all code paths should now be obtaining the page lock before +f->sem. + +Reported-by: Szabó Tamás +Tested-by: Thomas Betker +Signed-off-by: David Woodhouse +Signed-off-by: Greg Kroah-Hartman + +--- + fs/jffs2/README.Locking | 5 +---- + fs/jffs2/gc.c | 17 ++++++++++------- + 2 files changed, 11 insertions(+), 11 deletions(-) + +--- a/fs/jffs2/README.Locking ++++ b/fs/jffs2/README.Locking +@@ -2,10 +2,6 @@ + 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 + JFFS2. It is not expected to remain perfectly up to date, but ought to + be fairly close. +@@ -69,6 +65,7 @@ Ordering constraints: + any f->sem held. + 2. Never attempt to lock two file mutexes in one thread. + No ordering rules have been made for doing so. ++ 3. Never lock a page cache page with f->sem held. + + + erase_completion_lock spinlock +--- a/fs/jffs2/gc.c ++++ b/fs/jffs2/gc.c +@@ -1296,14 +1296,17 @@ static int jffs2_garbage_collect_dnode(s + 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); ++ mutex_lock(&f->sem); + + if (IS_ERR(pg_ptr)) { + pr_warn("read_cache_page() returned error: %ld\n", diff --git a/queue-3.10/revert-jffs2-fix-lock-acquisition-order-bug-in-jffs2_write_begin.patch b/queue-3.10/revert-jffs2-fix-lock-acquisition-order-bug-in-jffs2_write_begin.patch new file mode 100644 index 00000000000..9cfeec4ffd0 --- /dev/null +++ b/queue-3.10/revert-jffs2-fix-lock-acquisition-order-bug-in-jffs2_write_begin.patch @@ -0,0 +1,133 @@ +From 157078f64b8a9cd7011b6b900b2f2498df850748 Mon Sep 17 00:00:00 2001 +From: Thomas Betker +Date: Tue, 10 Nov 2015 22:18:15 +0100 +Subject: Revert "jffs2: Fix lock acquisition order bug in jffs2_write_begin" + +From: Thomas Betker + +commit 157078f64b8a9cd7011b6b900b2f2498df850748 upstream. + +This reverts commit 5ffd3412ae55 +("jffs2: Fix lock acquisition order bug in jffs2_write_begin"). + +The commit modified jffs2_write_begin() to remove a deadlock with +jffs2_garbage_collect_live(), but this introduced new deadlocks found +by multiple users. page_lock() actually has to be called before +mutex_lock(&c->alloc_sem) or mutex_lock(&f->sem) because +jffs2_write_end() and jffs2_readpage() are called with the page locked, +and they acquire c->alloc_sem and f->sem, resp. + +In other words, the lock order in jffs2_write_begin() was correct, and +it is the jffs2_garbage_collect_live() path that has to be changed. + +Revert the commit to get rid of the new deadlocks, and to clear the way +for a better fix of the original deadlock. + +Reported-by: Deng Chao +Reported-by: Ming Liu +Reported-by: wangzaiwei +Signed-off-by: Thomas Betker +Signed-off-by: David Woodhouse +Signed-off-by: Greg Kroah-Hartman + +--- + fs/jffs2/file.c | 39 ++++++++++++++++++--------------------- + 1 file changed, 18 insertions(+), 21 deletions(-) + +--- a/fs/jffs2/file.c ++++ b/fs/jffs2/file.c +@@ -138,39 +138,33 @@ static int jffs2_write_begin(struct file + struct page *pg; + struct inode *inode = mapping->host; + struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); +- struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); +- struct jffs2_raw_inode ri; +- uint32_t alloc_len = 0; + pgoff_t index = pos >> PAGE_CACHE_SHIFT; + uint32_t pageofs = index << PAGE_CACHE_SHIFT; + int ret = 0; + +- jffs2_dbg(1, "%s()\n", __func__); +- +- if (pageofs > inode->i_size) { +- ret = jffs2_reserve_space(c, sizeof(ri), &alloc_len, +- ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE); +- if (ret) +- return ret; +- } +- +- mutex_lock(&f->sem); + pg = grab_cache_page_write_begin(mapping, index, flags); +- if (!pg) { +- if (alloc_len) +- jffs2_complete_reservation(c); +- mutex_unlock(&f->sem); ++ if (!pg) + return -ENOMEM; +- } + *pagep = pg; + +- if (alloc_len) { ++ jffs2_dbg(1, "%s()\n", __func__); ++ ++ if (pageofs > inode->i_size) { + /* Make new hole frag from old EOF to new page */ ++ struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); ++ struct jffs2_raw_inode ri; + struct jffs2_full_dnode *fn; ++ uint32_t alloc_len; + + jffs2_dbg(1, "Writing new hole frag 0x%x-0x%x between current EOF and new page\n", + (unsigned int)inode->i_size, pageofs); + ++ ret = jffs2_reserve_space(c, sizeof(ri), &alloc_len, ++ ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE); ++ if (ret) ++ goto out_page; ++ ++ mutex_lock(&f->sem); + memset(&ri, 0, sizeof(ri)); + + ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); +@@ -197,6 +191,7 @@ static int jffs2_write_begin(struct file + if (IS_ERR(fn)) { + ret = PTR_ERR(fn); + jffs2_complete_reservation(c); ++ mutex_unlock(&f->sem); + goto out_page; + } + ret = jffs2_add_full_dnode_to_inode(c, f, fn); +@@ -211,10 +206,12 @@ static int jffs2_write_begin(struct file + jffs2_mark_node_obsolete(c, fn->raw); + jffs2_free_full_dnode(fn); + jffs2_complete_reservation(c); ++ mutex_unlock(&f->sem); + goto out_page; + } + jffs2_complete_reservation(c); + inode->i_size = pageofs; ++ mutex_unlock(&f->sem); + } + + /* +@@ -223,18 +220,18 @@ static int jffs2_write_begin(struct file + * case of a short-copy. + */ + if (!PageUptodate(pg)) { ++ mutex_lock(&f->sem); + ret = jffs2_do_readpage_nolock(inode, pg); ++ mutex_unlock(&f->sem); + if (ret) + goto out_page; + } +- mutex_unlock(&f->sem); + jffs2_dbg(1, "end write_begin(). pg->flags %lx\n", pg->flags); + return ret; + + out_page: + unlock_page(pg); + page_cache_release(pg); +- mutex_unlock(&f->sem); + return ret; + } + diff --git a/queue-3.10/series b/queue-3.10/series index b130535bca1..f6edc7cb0af 100644 --- a/queue-3.10/series +++ b/queue-3.10/series @@ -6,3 +6,6 @@ drm-ast-fix-incorrect-register-check-for-dram-width.patch libata-fix-hdio_get_32bit-ioctl.patch libata-align-ata_device-s-id-on-a-cacheline.patch pm-sleep-x86-fix-crash-on-graph-trace-through-x86-suspend.patch +revert-jffs2-fix-lock-acquisition-order-bug-in-jffs2_write_begin.patch +jffs2-fix-page-lock-f-sem-deadlock.patch +fix-directory-hardlinks-from-deleted-directories.patch