summaryrefslogtreecommitdiff
path: root/fs
diff options
context:
space:
mode:
Diffstat (limited to 'fs')
-rw-r--r--fs/aufs/branch.c15
-rw-r--r--fs/aufs/dbgaufs.c3
-rw-r--r--fs/aufs/dcsub.c3
-rw-r--r--fs/aufs/dentry.c8
-rw-r--r--fs/aufs/dentry.h2
-rw-r--r--fs/aufs/dinfo.c5
-rw-r--r--fs/aufs/file.c12
-rw-r--r--fs/aufs/file.h2
-rw-r--r--fs/aufs/finfo.c4
-rw-r--r--fs/aufs/iinfo.c5
-rw-r--r--fs/aufs/inode.c6
-rw-r--r--fs/aufs/inode.h2
-rw-r--r--fs/aufs/loop.c3
-rw-r--r--fs/aufs/module.c60
-rw-r--r--fs/aufs/module.h14
-rw-r--r--fs/aufs/sbinfo.c5
-rw-r--r--fs/aufs/super.h2
-rw-r--r--fs/aufs/vdir.c11
-rw-r--r--fs/btrfs/ctree.h1
-rw-r--r--fs/btrfs/disk-io.c7
-rw-r--r--fs/btrfs/disk-io.h2
-rw-r--r--fs/btrfs/ioctl.c2
-rw-r--r--fs/btrfs/qgroup.c21
-rw-r--r--fs/btrfs/qgroup.h3
-rw-r--r--fs/btrfs/root-tree.c27
-rw-r--r--fs/seq_file.c4
-rw-r--r--fs/sysfs/file.c8
27 files changed, 175 insertions, 62 deletions
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);
}