diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/aufs/branch.c | 15 | ||||
-rw-r--r-- | fs/aufs/dbgaufs.c | 3 | ||||
-rw-r--r-- | fs/aufs/dcsub.c | 3 | ||||
-rw-r--r-- | fs/aufs/dentry.c | 8 | ||||
-rw-r--r-- | fs/aufs/dentry.h | 2 | ||||
-rw-r--r-- | fs/aufs/dinfo.c | 5 | ||||
-rw-r--r-- | fs/aufs/file.c | 12 | ||||
-rw-r--r-- | fs/aufs/file.h | 2 | ||||
-rw-r--r-- | fs/aufs/finfo.c | 4 | ||||
-rw-r--r-- | fs/aufs/iinfo.c | 5 | ||||
-rw-r--r-- | fs/aufs/inode.c | 6 | ||||
-rw-r--r-- | fs/aufs/inode.h | 2 | ||||
-rw-r--r-- | fs/aufs/loop.c | 3 | ||||
-rw-r--r-- | fs/aufs/module.c | 60 | ||||
-rw-r--r-- | fs/aufs/module.h | 14 | ||||
-rw-r--r-- | fs/aufs/sbinfo.c | 5 | ||||
-rw-r--r-- | fs/aufs/super.h | 2 | ||||
-rw-r--r-- | fs/aufs/vdir.c | 11 | ||||
-rw-r--r-- | fs/btrfs/ctree.h | 1 | ||||
-rw-r--r-- | fs/btrfs/disk-io.c | 7 | ||||
-rw-r--r-- | fs/btrfs/disk-io.h | 2 | ||||
-rw-r--r-- | fs/btrfs/ioctl.c | 2 | ||||
-rw-r--r-- | fs/btrfs/qgroup.c | 21 | ||||
-rw-r--r-- | fs/btrfs/qgroup.h | 3 | ||||
-rw-r--r-- | fs/btrfs/root-tree.c | 27 | ||||
-rw-r--r-- | fs/seq_file.c | 4 | ||||
-rw-r--r-- | fs/sysfs/file.c | 8 |
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); } |