From 0520a938e11c34a5ffc422b9316b85e294b0fbb2 Mon Sep 17 00:00:00 2001
From: André Fabian Silva Delgado <emulatorman@parabola.nu>
Date: Sun, 11 Sep 2016 12:58:59 -0300
Subject: Linux-libre 4.7.3-gnu

---
 fs/aufs/branch.c     | 15 +++++++------
 fs/aufs/dbgaufs.c    |  3 +++
 fs/aufs/dcsub.c      |  3 ++-
 fs/aufs/dentry.c     |  8 ++++---
 fs/aufs/dentry.h     |  2 +-
 fs/aufs/dinfo.c      |  5 +++--
 fs/aufs/file.c       | 12 ++++++++---
 fs/aufs/file.h       |  2 +-
 fs/aufs/finfo.c      |  4 ++--
 fs/aufs/iinfo.c      |  5 +++--
 fs/aufs/inode.c      |  6 ++++--
 fs/aufs/inode.h      |  2 +-
 fs/aufs/loop.c       |  3 ++-
 fs/aufs/module.c     | 60 +++++++++++++++++++++++++++++++++++++++++++++-------
 fs/aufs/module.h     | 14 +++++++++++-
 fs/aufs/sbinfo.c     |  5 +++--
 fs/aufs/super.h      |  2 +-
 fs/aufs/vdir.c       | 11 +++++-----
 fs/btrfs/ctree.h     |  1 +
 fs/btrfs/disk-io.c   |  7 +++---
 fs/btrfs/disk-io.h   |  2 ++
 fs/btrfs/ioctl.c     |  2 +-
 fs/btrfs/qgroup.c    | 21 ++++++++++++++----
 fs/btrfs/qgroup.h    |  3 ++-
 fs/btrfs/root-tree.c | 27 +++++++++++++++--------
 fs/seq_file.c        |  4 +++-
 fs/sysfs/file.c      |  8 ++++++-
 27 files changed, 175 insertions(+), 62 deletions(-)

(limited to 'fs')

diff --git a/fs/aufs/branch.c b/fs/aufs/branch.c
index 80b018f57..3bfbe5b51 100644
--- a/fs/aufs/branch.c
+++ b/fs/aufs/branch.c
@@ -141,12 +141,12 @@ static struct au_branch *au_br_alloc(struct super_block *sb, int new_nbranch,
 			goto out_wbr;
 	}
 
-	err = au_sbr_realloc(au_sbi(sb), new_nbranch);
+	err = au_sbr_realloc(au_sbi(sb), new_nbranch, /*may_shrink*/0);
 	if (!err)
-		err = au_di_realloc(au_di(root), new_nbranch);
+		err = au_di_realloc(au_di(root), new_nbranch, /*may_shrink*/0);
 	if (!err) {
 		inode = d_inode(root);
-		err = au_hinode_realloc(au_ii(inode), new_nbranch);
+		err = au_hinode_realloc(au_ii(inode), new_nbranch, /*may_shrink*/0);
 	}
 	if (!err)
 		return add_branch; /* success */
@@ -890,7 +890,8 @@ static void au_br_do_del_brp(struct au_sbinfo *sbinfo,
 	sbinfo->si_branch[0 + bbot] = NULL;
 	sbinfo->si_bbot--;
 
-	p = krealloc(sbinfo->si_branch, sizeof(*p) * bbot, AuGFP_SBILIST);
+	p = au_krealloc(sbinfo->si_branch, sizeof(*p) * bbot, AuGFP_SBILIST,
+			/*may_shrink*/1);
 	if (p)
 		sbinfo->si_branch = p;
 	/* harmless error */
@@ -909,7 +910,8 @@ static void au_br_do_del_hdp(struct au_dinfo *dinfo, const aufs_bindex_t bindex,
 	/* au_h_dentry_init(au_hdentry(dinfo, bbot); */
 	dinfo->di_bbot--;
 
-	p = krealloc(dinfo->di_hdentry, sizeof(*p) * bbot, AuGFP_SBILIST);
+	p = au_krealloc(dinfo->di_hdentry, sizeof(*p) * bbot, AuGFP_SBILIST,
+			/*may_shrink*/1);
 	if (p)
 		dinfo->di_hdentry = p;
 	/* harmless error */
@@ -928,7 +930,8 @@ static void au_br_do_del_hip(struct au_iinfo *iinfo, const aufs_bindex_t bindex,
 	/* au_hinode_init(au_hinode(iinfo, bbot)); */
 	iinfo->ii_bbot--;
 
-	p = krealloc(iinfo->ii_hinode, sizeof(*p) * bbot, AuGFP_SBILIST);
+	p = au_krealloc(iinfo->ii_hinode, sizeof(*p) * bbot, AuGFP_SBILIST,
+			/*may_shrink*/1);
 	if (p)
 		iinfo->ii_hinode = p;
 	/* harmless error */
diff --git a/fs/aufs/dbgaufs.c b/fs/aufs/dbgaufs.c
index f85a813a4..4db01e853 100644
--- a/fs/aufs/dbgaufs.c
+++ b/fs/aufs/dbgaufs.c
@@ -256,7 +256,10 @@ void dbgaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex)
 	for (; bindex <= bbot; bindex++) {
 		br = au_sbr(sb, bindex);
 		xi = &br->br_xino;
+		/* debugfs acquires the parent i_mutex */
+		lockdep_off();
 		debugfs_remove(xi->xi_dbgaufs);
+		lockdep_on();
 		xi->xi_dbgaufs = NULL;
 	}
 }
diff --git a/fs/aufs/dcsub.c b/fs/aufs/dcsub.c
index 09bc36af7..5d4639838 100644
--- a/fs/aufs/dcsub.c
+++ b/fs/aufs/dcsub.c
@@ -69,7 +69,8 @@ static int au_dpages_append(struct au_dcsub_pages *dpages,
 		err = -ENOMEM;
 		sz = dpages->ndpage * sizeof(*dpages->dpages);
 		p = au_kzrealloc(dpages->dpages, sz,
-				 sz + sizeof(*dpages->dpages), gfp);
+				 sz + sizeof(*dpages->dpages), gfp,
+				 /*may_shrink*/0);
 		if (unlikely(!p))
 			goto out;
 
diff --git a/fs/aufs/dentry.c b/fs/aufs/dentry.c
index 51af2cc72..5eb4f3620 100644
--- a/fs/aufs/dentry.c
+++ b/fs/aufs/dentry.c
@@ -36,7 +36,7 @@ au_do_lookup(struct dentry *h_parent, struct dentry *dentry,
 	br = au_sbr(dentry->d_sb, bindex);
 	wh_able = !!au_br_whable(br->br_perm);
 	if (wh_able)
-		wh_found = au_wh_test(h_parent, wh_name, /*try_sio*/0);
+		wh_found = au_wh_test(h_parent, wh_name, ignore_perm);
 	h_dentry = ERR_PTR(wh_found);
 	if (!wh_found)
 		goto real_lookup;
@@ -701,7 +701,7 @@ void au_refresh_dop(struct dentry *dentry, int force_reval)
 
 int au_refresh_dentry(struct dentry *dentry, struct dentry *parent)
 {
-	int err, ebrange;
+	int err, ebrange, nbr;
 	unsigned int sigen;
 	struct au_dinfo *dinfo, *tmp;
 	struct super_block *sb;
@@ -717,8 +717,9 @@ int au_refresh_dentry(struct dentry *dentry, struct dentry *parent)
 	if (unlikely(err))
 		goto out;
 
+	nbr = au_sbbot(sb) + 1;
 	dinfo = au_di(dentry);
-	err = au_di_realloc(dinfo, au_sbbot(sb) + 1);
+	err = au_di_realloc(dinfo, nbr, /*may_shrink*/0);
 	if (unlikely(err))
 		goto out;
 	ebrange = au_dbrange_test(dentry);
@@ -761,6 +762,7 @@ int au_refresh_dentry(struct dentry *dentry, struct dentry *parent)
 		au_dbg_verify_dinode(dentry);
 		AuTraceErr(err);
 	}
+	au_di_realloc(dinfo, nbr, /*may_shrink*/1); /* harmless if err */
 	au_rw_write_unlock(&tmp->di_rwsem);
 	au_di_free(tmp);
 	if (unlikely(err))
diff --git a/fs/aufs/dentry.h b/fs/aufs/dentry.h
index dc027191c..01211d220 100644
--- a/fs/aufs/dentry.h
+++ b/fs/aufs/dentry.h
@@ -66,7 +66,7 @@ void au_di_swap(struct au_dinfo *a, struct au_dinfo *b);
 void au_di_cp(struct au_dinfo *dst, struct au_dinfo *src);
 int au_di_init(struct dentry *dentry);
 void au_di_fin(struct dentry *dentry);
-int au_di_realloc(struct au_dinfo *dinfo, int nbr);
+int au_di_realloc(struct au_dinfo *dinfo, int nbr, int may_shrink);
 
 void di_read_lock(struct dentry *d, int flags, unsigned int lsc);
 void di_read_unlock(struct dentry *d, int flags);
diff --git a/fs/aufs/dinfo.c b/fs/aufs/dinfo.c
index 361e86142..2626c0cca 100644
--- a/fs/aufs/dinfo.c
+++ b/fs/aufs/dinfo.c
@@ -129,7 +129,7 @@ void au_di_fin(struct dentry *dentry)
 	au_di_free(dinfo);
 }
 
-int au_di_realloc(struct au_dinfo *dinfo, int nbr)
+int au_di_realloc(struct au_dinfo *dinfo, int nbr, int may_shrink)
 {
 	int err, sz;
 	struct au_hdentry *hdp;
@@ -140,7 +140,8 @@ int au_di_realloc(struct au_dinfo *dinfo, int nbr)
 	sz = sizeof(*hdp) * (dinfo->di_bbot + 1);
 	if (!sz)
 		sz = sizeof(*hdp);
-	hdp = au_kzrealloc(dinfo->di_hdentry, sz, sizeof(*hdp) * nbr, GFP_NOFS);
+	hdp = au_kzrealloc(dinfo->di_hdentry, sz, sizeof(*hdp) * nbr, GFP_NOFS,
+			   may_shrink);
 	if (hdp) {
 		dinfo->di_hdentry = hdp;
 		err = 0;
diff --git a/fs/aufs/file.c b/fs/aufs/file.c
index 429b56840..8d5ca6f7f 100644
--- a/fs/aufs/file.c
+++ b/fs/aufs/file.c
@@ -655,23 +655,26 @@ static void au_do_refresh_dir(struct file *file)
  */
 static int refresh_file(struct file *file, int (*reopen)(struct file *file))
 {
-	int err, need_reopen;
+	int err, need_reopen, nbr;
 	aufs_bindex_t bbot, bindex;
 	struct dentry *dentry;
+	struct super_block *sb;
 	struct au_finfo *finfo;
 	struct au_hfile *hfile;
 
 	dentry = file->f_path.dentry;
+	sb = dentry->d_sb;
+	nbr = au_sbbot(sb) + 1;
 	finfo = au_fi(file);
 	if (!finfo->fi_hdir) {
 		hfile = &finfo->fi_htop;
 		AuDebugOn(!hfile->hf_file);
-		bindex = au_br_index(dentry->d_sb, hfile->hf_br->br_id);
+		bindex = au_br_index(sb, hfile->hf_br->br_id);
 		AuDebugOn(bindex < 0);
 		if (bindex != finfo->fi_btop)
 			au_set_fbtop(file, bindex);
 	} else {
-		err = au_fidir_realloc(finfo, au_sbbot(dentry->d_sb) + 1);
+		err = au_fidir_realloc(finfo, nbr, /*may_shrink*/0);
 		if (unlikely(err))
 			goto out;
 		au_do_refresh_dir(file);
@@ -681,6 +684,9 @@ static int refresh_file(struct file *file, int (*reopen)(struct file *file))
 	need_reopen = 1;
 	if (!au_test_mmapped(file))
 		err = au_file_refresh_by_inode(file, &need_reopen);
+	if (finfo->fi_hdir)
+		/* harmless if err */
+		au_fidir_realloc(finfo, nbr, /*may_shrink*/1);
 	if (!err && need_reopen && !d_unlinked(dentry))
 		err = reopen(file);
 	if (!err) {
diff --git a/fs/aufs/file.h b/fs/aufs/file.h
index 6ecd0df33..f53b381a7 100644
--- a/fs/aufs/file.h
+++ b/fs/aufs/file.h
@@ -110,7 +110,7 @@ void au_set_h_fptr(struct file *file, aufs_bindex_t bindex,
 
 void au_update_figen(struct file *file);
 struct au_fidir *au_fidir_alloc(struct super_block *sb);
-int au_fidir_realloc(struct au_finfo *finfo, int nbr);
+int au_fidir_realloc(struct au_finfo *finfo, int nbr, int may_shrink);
 
 void au_fi_init_once(void *_fi);
 void au_finfo_fin(struct file *file, int atonce);
diff --git a/fs/aufs/finfo.c b/fs/aufs/finfo.c
index e0c16b2f1..0f016ed54 100644
--- a/fs/aufs/finfo.c
+++ b/fs/aufs/finfo.c
@@ -66,7 +66,7 @@ struct au_fidir *au_fidir_alloc(struct super_block *sb)
 	return fidir;
 }
 
-int au_fidir_realloc(struct au_finfo *finfo, int nbr)
+int au_fidir_realloc(struct au_finfo *finfo, int nbr, int may_shrink)
 {
 	int err;
 	struct au_fidir *fidir, *p;
@@ -77,7 +77,7 @@ int au_fidir_realloc(struct au_finfo *finfo, int nbr)
 
 	err = -ENOMEM;
 	p = au_kzrealloc(fidir, au_fidir_sz(fidir->fd_nent), au_fidir_sz(nbr),
-			 GFP_NOFS);
+			 GFP_NOFS, may_shrink);
 	if (p) {
 		p->fd_nent = nbr;
 		finfo->fi_hdir = p;
diff --git a/fs/aufs/iinfo.c b/fs/aufs/iinfo.c
index c83db695d..975cff389 100644
--- a/fs/aufs/iinfo.c
+++ b/fs/aufs/iinfo.c
@@ -205,7 +205,7 @@ int au_iinfo_init(struct inode *inode)
 	return -ENOMEM;
 }
 
-int au_hinode_realloc(struct au_iinfo *iinfo, int nbr)
+int au_hinode_realloc(struct au_iinfo *iinfo, int nbr, int may_shrink)
 {
 	int err, i;
 	struct au_hinode *hip;
@@ -213,7 +213,8 @@ int au_hinode_realloc(struct au_iinfo *iinfo, int nbr)
 	AuRwMustWriteLock(&iinfo->ii_rwsem);
 
 	err = -ENOMEM;
-	hip = krealloc(iinfo->ii_hinode, sizeof(*hip) * nbr, GFP_NOFS);
+	hip = au_krealloc(iinfo->ii_hinode, sizeof(*hip) * nbr, GFP_NOFS,
+			  may_shrink);
 	if (hip) {
 		iinfo->ii_hinode = hip;
 		i = iinfo->ii_bbot + 1;
diff --git a/fs/aufs/inode.c b/fs/aufs/inode.c
index a4b994e8c..8f7446df6 100644
--- a/fs/aufs/inode.c
+++ b/fs/aufs/inode.c
@@ -27,7 +27,7 @@ static void au_refresh_hinode_attr(struct inode *inode, int do_version)
 
 static int au_ii_refresh(struct inode *inode, int *update)
 {
-	int err, e;
+	int err, e, nbr;
 	umode_t type;
 	aufs_bindex_t bindex, new_bindex;
 	struct super_block *sb;
@@ -39,9 +39,10 @@ static int au_ii_refresh(struct inode *inode, int *update)
 
 	*update = 0;
 	sb = inode->i_sb;
+	nbr = au_sbbot(sb) + 1;
 	type = inode->i_mode & S_IFMT;
 	iinfo = au_ii(inode);
-	err = au_hinode_realloc(iinfo, au_sbbot(sb) + 1);
+	err = au_hinode_realloc(iinfo, nbr, /*may_shrink*/0);
 	if (unlikely(err))
 		goto out;
 
@@ -79,6 +80,7 @@ static int au_ii_refresh(struct inode *inode, int *update)
 		}
 	}
 	au_update_ibrange(inode, /*do_put_zero*/0);
+	au_hinode_realloc(iinfo, nbr, /*may_shrink*/1); /* harmless if err */
 	e = au_dy_irefresh(inode);
 	if (unlikely(e && !err))
 		err = e;
diff --git a/fs/aufs/inode.h b/fs/aufs/inode.h
index 60995c054..33d9f5e8f 100644
--- a/fs/aufs/inode.h
+++ b/fs/aufs/inode.h
@@ -258,7 +258,7 @@ void au_icntnr_init_once(void *_c);
 void au_hinode_init(struct au_hinode *hinode);
 int au_iinfo_init(struct inode *inode);
 void au_iinfo_fin(struct inode *inode);
-int au_hinode_realloc(struct au_iinfo *iinfo, int nbr);
+int au_hinode_realloc(struct au_iinfo *iinfo, int nbr, int may_shrink);
 
 #ifdef CONFIG_PROC_FS
 /* plink.c */
diff --git a/fs/aufs/loop.c b/fs/aufs/loop.c
index e92a34599..c3ca50f16 100644
--- a/fs/aufs/loop.c
+++ b/fs/aufs/loop.c
@@ -91,7 +91,8 @@ void au_warn_loopback(struct super_block *h_sb)
 	new_nelem = au_warn_loopback_nelem + au_warn_loopback_step;
 	a = au_kzrealloc(au_warn_loopback_array,
 			 au_warn_loopback_nelem * sizeof(unsigned long),
-			 new_nelem * sizeof(unsigned long), GFP_ATOMIC);
+			 new_nelem * sizeof(unsigned long), GFP_ATOMIC,
+			 /*may_shrink*/0);
 	if (a) {
 		au_warn_loopback_nelem = new_nelem;
 		au_warn_loopback_array = a;
diff --git a/fs/aufs/module.c b/fs/aufs/module.c
index ede9a232a..5092b4bd6 100644
--- a/fs/aufs/module.c
+++ b/fs/aufs/module.c
@@ -10,13 +10,57 @@
 #include <linux/seq_file.h>
 #include "aufs.h"
 
-void *au_kzrealloc(void *p, unsigned int nused, unsigned int new_sz, gfp_t gfp)
+/* shrinkable realloc */
+void *au_krealloc(void *p, unsigned int new_sz, gfp_t gfp, int may_shrink)
 {
-	if (new_sz <= nused)
-		return p;
+	size_t sz;
+	int diff;
+
+	sz = 0;
+	diff = -1;
+	if (p) {
+#if 0 /* unused */
+		if (!new_sz) {
+			au_delayed_kfree(p);
+			p = NULL;
+			goto out;
+		}
+#else
+		AuDebugOn(!new_sz);
+#endif
+		sz = ksize(p);
+		diff = au_kmidx_sub(sz, new_sz);
+	}
+	if (sz && !diff)
+		goto out;
+
+	if (sz < new_sz)
+		/* expand or SLOB */
+		p = krealloc(p, new_sz, gfp);
+	else if (new_sz < sz && may_shrink) {
+		/* shrink */
+		void *q;
+
+		q = kmalloc(new_sz, gfp);
+		if (q) {
+			if (p) {
+				memcpy(q, p, new_sz);
+				au_delayed_kfree(p);
+			}
+			p = q;
+		} else
+			p = NULL;
+	}
 
-	p = krealloc(p, new_sz, gfp);
-	if (p)
+out:
+	return p;
+}
+
+void *au_kzrealloc(void *p, unsigned int nused, unsigned int new_sz, gfp_t gfp,
+		   int may_shrink)
+{
+	p = au_krealloc(p, new_sz, gfp, may_shrink);
+	if (p && new_sz > nused)
 		memset(p + nused, 0, new_sz - nused);
 	return p;
 }
@@ -38,9 +82,9 @@ static void au_do_dfree(struct work_struct *work __maybe_unused)
 		head = &au_dfree.cache[AuCache_##idx].llist;		\
 		node = llist_del_all(head);				\
 		for (; node; node = next) {				\
-			struct au_##name *p =				\
-				p = llist_entry(node, struct au_##name,	\
-						lnode);			\
+			struct au_##name *p				\
+				= llist_entry(node, struct au_##name,	\
+					      lnode);			\
 			next = llist_next(node);			\
 			au_cache_free_##name(p);			\
 		}							\
diff --git a/fs/aufs/module.h b/fs/aufs/module.h
index 681dc75f5..6f968ba41 100644
--- a/fs/aufs/module.h
+++ b/fs/aufs/module.h
@@ -25,7 +25,19 @@ extern bool au_userns;
 
 extern int au_dir_roflags;
 
-void *au_kzrealloc(void *p, unsigned int nused, unsigned int new_sz, gfp_t gfp);
+void *au_krealloc(void *p, unsigned int new_sz, gfp_t gfp, int may_shrink);
+void *au_kzrealloc(void *p, unsigned int nused, unsigned int new_sz, gfp_t gfp,
+		   int may_shrink);
+
+static inline int au_kmidx_sub(size_t sz, size_t new_sz)
+{
+#ifndef CONFIG_SLOB
+	return kmalloc_index(sz) - kmalloc_index(new_sz);
+#else
+	return -1; /* SLOB is untested */
+#endif
+}
+
 int au_seq_path(struct seq_file *seq, struct path *path);
 
 #ifdef CONFIG_PROC_FS
diff --git a/fs/aufs/sbinfo.c b/fs/aufs/sbinfo.c
index 25f60549c..e113b5119 100644
--- a/fs/aufs/sbinfo.c
+++ b/fs/aufs/sbinfo.c
@@ -118,7 +118,7 @@ out:
 	return err;
 }
 
-int au_sbr_realloc(struct au_sbinfo *sbinfo, int nbr)
+int au_sbr_realloc(struct au_sbinfo *sbinfo, int nbr, int may_shrink)
 {
 	int err, sz;
 	struct au_branch **brp;
@@ -129,7 +129,8 @@ int au_sbr_realloc(struct au_sbinfo *sbinfo, int nbr)
 	sz = sizeof(*brp) * (sbinfo->si_bbot + 1);
 	if (unlikely(!sz))
 		sz = sizeof(*brp);
-	brp = au_kzrealloc(sbinfo->si_branch, sz, sizeof(*brp) * nbr, GFP_NOFS);
+	brp = au_kzrealloc(sbinfo->si_branch, sz, sizeof(*brp) * nbr, GFP_NOFS,
+			   may_shrink);
 	if (brp) {
 		sbinfo->si_branch = brp;
 		err = 0;
diff --git a/fs/aufs/super.h b/fs/aufs/super.h
index b8ca14994..e4ac866e7 100644
--- a/fs/aufs/super.h
+++ b/fs/aufs/super.h
@@ -266,7 +266,7 @@ void au_iarray_free(struct inode **a, unsigned long long max);
 /* sbinfo.c */
 void au_si_free(struct kobject *kobj);
 int au_si_alloc(struct super_block *sb);
-int au_sbr_realloc(struct au_sbinfo *sbinfo, int nbr);
+int au_sbr_realloc(struct au_sbinfo *sbinfo, int nbr, int may_shrink);
 
 unsigned int au_sigen_inc(struct super_block *sb);
 aufs_bindex_t au_new_br_id(struct super_block *sb);
diff --git a/fs/aufs/vdir.c b/fs/aufs/vdir.c
index e6e3a1eaa..b2eb4c058 100644
--- a/fs/aufs/vdir.c
+++ b/fs/aufs/vdir.c
@@ -266,8 +266,8 @@ static int append_deblk(struct au_vdir *vdir)
 	unsigned char **o;
 
 	err = -ENOMEM;
-	o = krealloc(vdir->vd_deblk, sizeof(*o) * (vdir->vd_nblk + 1),
-		     GFP_NOFS);
+	o = au_krealloc(vdir->vd_deblk, sizeof(*o) * (vdir->vd_nblk + 1),
+			GFP_NOFS, /*may_shrink*/0);
 	if (unlikely(!o))
 		goto out;
 
@@ -690,8 +690,8 @@ static int copy_vdir(struct au_vdir *tgt, struct au_vdir *src)
 	if (tgt->vd_nblk < src->vd_nblk) {
 		unsigned char **p;
 
-		p = krealloc(tgt->vd_deblk, sizeof(*p) * src->vd_nblk,
-			     GFP_NOFS);
+		p = au_krealloc(tgt->vd_deblk, sizeof(*p) * src->vd_nblk,
+				GFP_NOFS, /*may_shrink*/0);
 		if (unlikely(!p))
 			goto out;
 		tgt->vd_deblk = p;
@@ -701,7 +701,8 @@ static int copy_vdir(struct au_vdir *tgt, struct au_vdir *src)
 		unsigned char *p;
 
 		tgt->vd_deblk_sz = deblk_sz;
-		p = krealloc(tgt->vd_deblk[0], deblk_sz, GFP_NOFS);
+		p = au_krealloc(tgt->vd_deblk[0], deblk_sz, GFP_NOFS,
+				/*may_shrink*/1);
 		if (unlikely(!p))
 			goto out;
 		tgt->vd_deblk[0] = p;
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 4274a7bfd..72f50480e 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -1040,6 +1040,7 @@ struct btrfs_fs_info {
 	struct btrfs_workqueue *qgroup_rescan_workers;
 	struct completion qgroup_rescan_completion;
 	struct btrfs_work qgroup_rescan_work;
+	bool qgroup_rescan_running;	/* protected by qgroup_rescan_lock */
 
 	/* filesystem state */
 	unsigned long fs_state;
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index 60ce11903..864cf3be0 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -1626,8 +1626,8 @@ fail:
 	return ret;
 }
 
-static struct btrfs_root *btrfs_lookup_fs_root(struct btrfs_fs_info *fs_info,
-					       u64 root_id)
+struct btrfs_root *btrfs_lookup_fs_root(struct btrfs_fs_info *fs_info,
+					u64 root_id)
 {
 	struct btrfs_root *root;
 
@@ -2306,6 +2306,7 @@ static void btrfs_init_qgroup(struct btrfs_fs_info *fs_info)
 	fs_info->quota_enabled = 0;
 	fs_info->pending_quota_state = 0;
 	fs_info->qgroup_ulist = NULL;
+	fs_info->qgroup_rescan_running = false;
 	mutex_init(&fs_info->qgroup_rescan_lock);
 }
 
@@ -3849,7 +3850,7 @@ void close_ctree(struct btrfs_root *root)
 	smp_mb();
 
 	/* wait for the qgroup rescan worker to stop */
-	btrfs_qgroup_wait_for_completion(fs_info);
+	btrfs_qgroup_wait_for_completion(fs_info, false);
 
 	/* wait for the uuid_scan task to finish */
 	down(&fs_info->uuid_tree_rescan_sem);
diff --git a/fs/btrfs/disk-io.h b/fs/btrfs/disk-io.h
index acba82149..355e31f90 100644
--- a/fs/btrfs/disk-io.h
+++ b/fs/btrfs/disk-io.h
@@ -68,6 +68,8 @@ struct extent_buffer *btrfs_find_tree_block(struct btrfs_fs_info *fs_info,
 struct btrfs_root *btrfs_read_fs_root(struct btrfs_root *tree_root,
 				      struct btrfs_key *location);
 int btrfs_init_fs_root(struct btrfs_root *root);
+struct btrfs_root *btrfs_lookup_fs_root(struct btrfs_fs_info *fs_info,
+					u64 root_id);
 int btrfs_insert_fs_root(struct btrfs_fs_info *fs_info,
 			 struct btrfs_root *root);
 void btrfs_free_fs_roots(struct btrfs_fs_info *fs_info);
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 05173563e..3722a1f65 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -5088,7 +5088,7 @@ static long btrfs_ioctl_quota_rescan_wait(struct file *file, void __user *arg)
 	if (!capable(CAP_SYS_ADMIN))
 		return -EPERM;
 
-	return btrfs_qgroup_wait_for_completion(root->fs_info);
+	return btrfs_qgroup_wait_for_completion(root->fs_info, true);
 }
 
 static long _btrfs_ioctl_set_received_subvol(struct file *file,
diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c
index 9d4c05b14..4904ebee4 100644
--- a/fs/btrfs/qgroup.c
+++ b/fs/btrfs/qgroup.c
@@ -995,7 +995,7 @@ int btrfs_quota_disable(struct btrfs_trans_handle *trans,
 		goto out;
 	fs_info->quota_enabled = 0;
 	fs_info->pending_quota_state = 0;
-	btrfs_qgroup_wait_for_completion(fs_info);
+	btrfs_qgroup_wait_for_completion(fs_info, false);
 	spin_lock(&fs_info->qgroup_lock);
 	quota_root = fs_info->quota_root;
 	fs_info->quota_root = NULL;
@@ -2302,6 +2302,10 @@ static void btrfs_qgroup_rescan_worker(struct btrfs_work *work)
 	int err = -ENOMEM;
 	int ret = 0;
 
+	mutex_lock(&fs_info->qgroup_rescan_lock);
+	fs_info->qgroup_rescan_running = true;
+	mutex_unlock(&fs_info->qgroup_rescan_lock);
+
 	path = btrfs_alloc_path();
 	if (!path)
 		goto out;
@@ -2368,6 +2372,9 @@ out:
 	}
 
 done:
+	mutex_lock(&fs_info->qgroup_rescan_lock);
+	fs_info->qgroup_rescan_running = false;
+	mutex_unlock(&fs_info->qgroup_rescan_lock);
 	complete_all(&fs_info->qgroup_rescan_completion);
 }
 
@@ -2486,20 +2493,26 @@ btrfs_qgroup_rescan(struct btrfs_fs_info *fs_info)
 	return 0;
 }
 
-int btrfs_qgroup_wait_for_completion(struct btrfs_fs_info *fs_info)
+int btrfs_qgroup_wait_for_completion(struct btrfs_fs_info *fs_info,
+				     bool interruptible)
 {
 	int running;
 	int ret = 0;
 
 	mutex_lock(&fs_info->qgroup_rescan_lock);
 	spin_lock(&fs_info->qgroup_lock);
-	running = fs_info->qgroup_flags & BTRFS_QGROUP_STATUS_FLAG_RESCAN;
+	running = fs_info->qgroup_rescan_running;
 	spin_unlock(&fs_info->qgroup_lock);
 	mutex_unlock(&fs_info->qgroup_rescan_lock);
 
-	if (running)
+	if (!running)
+		return 0;
+
+	if (interruptible)
 		ret = wait_for_completion_interruptible(
 					&fs_info->qgroup_rescan_completion);
+	else
+		wait_for_completion(&fs_info->qgroup_rescan_completion);
 
 	return ret;
 }
diff --git a/fs/btrfs/qgroup.h b/fs/btrfs/qgroup.h
index ecb2c143e..3d73e4c9c 100644
--- a/fs/btrfs/qgroup.h
+++ b/fs/btrfs/qgroup.h
@@ -46,7 +46,8 @@ int btrfs_quota_disable(struct btrfs_trans_handle *trans,
 			struct btrfs_fs_info *fs_info);
 int btrfs_qgroup_rescan(struct btrfs_fs_info *fs_info);
 void btrfs_qgroup_rescan_resume(struct btrfs_fs_info *fs_info);
-int btrfs_qgroup_wait_for_completion(struct btrfs_fs_info *fs_info);
+int btrfs_qgroup_wait_for_completion(struct btrfs_fs_info *fs_info,
+				     bool interruptible);
 int btrfs_add_qgroup_relation(struct btrfs_trans_handle *trans,
 			      struct btrfs_fs_info *fs_info, u64 src, u64 dst);
 int btrfs_del_qgroup_relation(struct btrfs_trans_handle *trans,
diff --git a/fs/btrfs/root-tree.c b/fs/btrfs/root-tree.c
index f1c30861d..3454aa4fa 100644
--- a/fs/btrfs/root-tree.c
+++ b/fs/btrfs/root-tree.c
@@ -272,6 +272,23 @@ int btrfs_find_orphan_roots(struct btrfs_root *tree_root)
 		root_key.objectid = key.offset;
 		key.offset++;
 
+		/*
+		 * The root might have been inserted already, as before we look
+		 * for orphan roots, log replay might have happened, which
+		 * triggers a transaction commit and qgroup accounting, which
+		 * in turn reads and inserts fs roots while doing backref
+		 * walking.
+		 */
+		root = btrfs_lookup_fs_root(tree_root->fs_info,
+					    root_key.objectid);
+		if (root) {
+			WARN_ON(!test_bit(BTRFS_ROOT_ORPHAN_ITEM_INSERTED,
+					  &root->state));
+			if (btrfs_root_refs(&root->root_item) == 0)
+				btrfs_add_dead_root(root);
+			continue;
+		}
+
 		root = btrfs_read_fs_root(tree_root, &root_key);
 		err = PTR_ERR_OR_ZERO(root);
 		if (err && err != -ENOENT) {
@@ -310,16 +327,8 @@ int btrfs_find_orphan_roots(struct btrfs_root *tree_root)
 		set_bit(BTRFS_ROOT_ORPHAN_ITEM_INSERTED, &root->state);
 
 		err = btrfs_insert_fs_root(root->fs_info, root);
-		/*
-		 * The root might have been inserted already, as before we look
-		 * for orphan roots, log replay might have happened, which
-		 * triggers a transaction commit and qgroup accounting, which
-		 * in turn reads and inserts fs roots while doing backref
-		 * walking.
-		 */
-		if (err == -EEXIST)
-			err = 0;
 		if (err) {
+			BUG_ON(err == -EEXIST);
 			btrfs_free_fs_root(root);
 			break;
 		}
diff --git a/fs/seq_file.c b/fs/seq_file.c
index 19f532e7d..6dc4296ee 100644
--- a/fs/seq_file.c
+++ b/fs/seq_file.c
@@ -223,8 +223,10 @@ ssize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
 		size -= n;
 		buf += n;
 		copied += n;
-		if (!m->count)
+		if (!m->count) {
+			m->from = 0;
 			m->index++;
+		}
 		if (!size)
 			goto Done;
 	}
diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c
index f35523d4f..b803213d1 100644
--- a/fs/sysfs/file.c
+++ b/fs/sysfs/file.c
@@ -114,9 +114,15 @@ static ssize_t sysfs_kf_read(struct kernfs_open_file *of, char *buf,
 	 * If buf != of->prealloc_buf, we don't know how
 	 * large it is, so cannot safely pass it to ->show
 	 */
-	if (pos || WARN_ON_ONCE(buf != of->prealloc_buf))
+	if (WARN_ON_ONCE(buf != of->prealloc_buf))
 		return 0;
 	len = ops->show(kobj, of->kn->priv, buf);
+	if (pos) {
+		if (len <= pos)
+			return 0;
+		len -= pos;
+		memmove(buf, buf + pos, len);
+	}
 	return min(count, len);
 }
 
-- 
cgit v1.2.3-54-g00ecf