On Tue 13-02-24 16:37:39, Chuck Lever wrote: > From: Chuck Lever <chuck.lever@xxxxxxxxxx> > > 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@xxxxxxxxxx> Makes sense. Feel free to add: Reviewed-by: Jan Kara <jack@xxxxxxx> 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) { > > -- Jan Kara <jack@xxxxxxxx> SUSE Labs, CR