Message ID | 170786025969.11135.16880338029664682984.stgit@91.116.238.104.host.secureserver.net (mailing list archive) |
---|---|
State | New |
Headers | show |
Series | Use Maple Trees for simple_offset utilities | expand |
On Tue 13-02-24 16:37:39, Chuck Lever wrote: > From: Chuck Lever <chuck.lever@oracle.com> > > For simple filesystems that use directory offset mapping, rely > strictly on the directory offset map to tell when a directory has > no children. > > After this patch is applied, the emptiness test holds only the RCU > read lock when the directory being tested has no children. > > In addition, this adds another layer of confirmation that > simple_offset_add/remove() are working as expected. > > Signed-off-by: Chuck Lever <chuck.lever@oracle.com> Makes sense. Feel free to add: Reviewed-by: Jan Kara <jack@suse.cz> Honza > --- > fs/libfs.c | 32 ++++++++++++++++++++++++++++++++ > include/linux/fs.h | 1 + > mm/shmem.c | 4 ++-- > 3 files changed, 35 insertions(+), 2 deletions(-) > > diff --git a/fs/libfs.c b/fs/libfs.c > index a38af72f4719..3cf773950f93 100644 > --- a/fs/libfs.c > +++ b/fs/libfs.c > @@ -313,6 +313,38 @@ void simple_offset_remove(struct offset_ctx *octx, struct dentry *dentry) > offset_set(dentry, 0); > } > > +/** > + * simple_offset_empty - Check if a dentry can be unlinked > + * @dentry: dentry to be tested > + * > + * Returns 0 if @dentry is a non-empty directory; otherwise returns 1. > + */ > +int simple_offset_empty(struct dentry *dentry) > +{ > + struct inode *inode = d_inode(dentry); > + struct offset_ctx *octx; > + struct dentry *child; > + unsigned long index; > + int ret = 1; > + > + if (!inode || !S_ISDIR(inode->i_mode)) > + return ret; > + > + index = 2; > + octx = inode->i_op->get_offset_ctx(inode); > + xa_for_each(&octx->xa, index, child) { > + spin_lock(&child->d_lock); > + if (simple_positive(child)) { > + spin_unlock(&child->d_lock); > + ret = 0; > + break; > + } > + spin_unlock(&child->d_lock); > + } > + > + return ret; > +} > + > /** > * simple_offset_rename_exchange - exchange rename with directory offsets > * @old_dir: parent of dentry being moved > diff --git a/include/linux/fs.h b/include/linux/fs.h > index ed5966a70495..03d141809a2c 100644 > --- a/include/linux/fs.h > +++ b/include/linux/fs.h > @@ -3267,6 +3267,7 @@ struct offset_ctx { > void simple_offset_init(struct offset_ctx *octx); > int simple_offset_add(struct offset_ctx *octx, struct dentry *dentry); > void simple_offset_remove(struct offset_ctx *octx, struct dentry *dentry); > +int simple_offset_empty(struct dentry *dentry); > int simple_offset_rename_exchange(struct inode *old_dir, > struct dentry *old_dentry, > struct inode *new_dir, > diff --git a/mm/shmem.c b/mm/shmem.c > index d7c84ff62186..6fed524343cb 100644 > --- a/mm/shmem.c > +++ b/mm/shmem.c > @@ -3374,7 +3374,7 @@ static int shmem_unlink(struct inode *dir, struct dentry *dentry) > > static int shmem_rmdir(struct inode *dir, struct dentry *dentry) > { > - if (!simple_empty(dentry)) > + if (!simple_offset_empty(dentry)) > return -ENOTEMPTY; > > drop_nlink(d_inode(dentry)); > @@ -3431,7 +3431,7 @@ static int shmem_rename2(struct mnt_idmap *idmap, > return simple_offset_rename_exchange(old_dir, old_dentry, > new_dir, new_dentry); > > - if (!simple_empty(new_dentry)) > + if (!simple_offset_empty(new_dentry)) > return -ENOTEMPTY; > > if (flags & RENAME_WHITEOUT) { > >
diff --git a/fs/libfs.c b/fs/libfs.c index a38af72f4719..3cf773950f93 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -313,6 +313,38 @@ void simple_offset_remove(struct offset_ctx *octx, struct dentry *dentry) offset_set(dentry, 0); } +/** + * simple_offset_empty - Check if a dentry can be unlinked + * @dentry: dentry to be tested + * + * Returns 0 if @dentry is a non-empty directory; otherwise returns 1. + */ +int simple_offset_empty(struct dentry *dentry) +{ + struct inode *inode = d_inode(dentry); + struct offset_ctx *octx; + struct dentry *child; + unsigned long index; + int ret = 1; + + if (!inode || !S_ISDIR(inode->i_mode)) + return ret; + + index = 2; + octx = inode->i_op->get_offset_ctx(inode); + xa_for_each(&octx->xa, index, child) { + spin_lock(&child->d_lock); + if (simple_positive(child)) { + spin_unlock(&child->d_lock); + ret = 0; + break; + } + spin_unlock(&child->d_lock); + } + + return ret; +} + /** * simple_offset_rename_exchange - exchange rename with directory offsets * @old_dir: parent of dentry being moved diff --git a/include/linux/fs.h b/include/linux/fs.h index ed5966a70495..03d141809a2c 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -3267,6 +3267,7 @@ struct offset_ctx { void simple_offset_init(struct offset_ctx *octx); int simple_offset_add(struct offset_ctx *octx, struct dentry *dentry); void simple_offset_remove(struct offset_ctx *octx, struct dentry *dentry); +int simple_offset_empty(struct dentry *dentry); int simple_offset_rename_exchange(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, diff --git a/mm/shmem.c b/mm/shmem.c index d7c84ff62186..6fed524343cb 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -3374,7 +3374,7 @@ static int shmem_unlink(struct inode *dir, struct dentry *dentry) static int shmem_rmdir(struct inode *dir, struct dentry *dentry) { - if (!simple_empty(dentry)) + if (!simple_offset_empty(dentry)) return -ENOTEMPTY; drop_nlink(d_inode(dentry)); @@ -3431,7 +3431,7 @@ static int shmem_rename2(struct mnt_idmap *idmap, return simple_offset_rename_exchange(old_dir, old_dentry, new_dir, new_dentry); - if (!simple_empty(new_dentry)) + if (!simple_offset_empty(new_dentry)) return -ENOTEMPTY; if (flags & RENAME_WHITEOUT) {