On 2019/2/14 23:34, Gao Xiang wrote: > In real scenario, there could be several threads accessing xattrs > of the same xattr-uninitialized inode, and init_inode_xattrs() > almost at the same time. Yeah, nice catch! > > That's actually an unexpected behavior, this patch closes the race. > > Fixes: b17500a0fdba ("staging: erofs: introduce xattr & acl support") > Cc: <stable@xxxxxxxxxxxxxxx> # 4.19+ > Signed-off-by: Gao Xiang <gaoxiang25@xxxxxxxxxx> > --- > drivers/staging/erofs/internal.h | 11 ++++++++--- > drivers/staging/erofs/xattr.c | 41 ++++++++++++++++++++++++++++------------ > 2 files changed, 37 insertions(+), 15 deletions(-) > > diff --git a/drivers/staging/erofs/internal.h b/drivers/staging/erofs/internal.h > index 3a27c255950b..e3bfde00c7d2 100644 > --- a/drivers/staging/erofs/internal.h > +++ b/drivers/staging/erofs/internal.h > @@ -327,12 +327,17 @@ static inline erofs_off_t iloc(struct erofs_sb_info *sbi, erofs_nid_t nid) > return blknr_to_addr(sbi->meta_blkaddr) + (nid << sbi->islotbits); > } > > -#define inode_set_inited_xattr(inode) (EROFS_V(inode)->flags |= 1) > -#define inode_has_inited_xattr(inode) (EROFS_V(inode)->flags & 1) > +/* atomic flag definitions */ > +#define EROFS_V_EA_INITED_BIT 0 > + > +/* bitlock definitions (arranged in reverse order) */ > +#define EROFS_V_BL_XATTR_BIT (BITS_PER_LONG - 1) > > struct erofs_vnode { > erofs_nid_t nid; > - unsigned int flags; > + > + /* atomic flags (including bitlocks) */ > + unsigned long flags; > > unsigned char data_mapping_mode; > /* inline size in bytes */ > diff --git a/drivers/staging/erofs/xattr.c b/drivers/staging/erofs/xattr.c > index 8c48b0ab31fd..a188aad00bf8 100644 > --- a/drivers/staging/erofs/xattr.c > +++ b/drivers/staging/erofs/xattr.c > @@ -44,18 +44,25 @@ static inline void xattr_iter_end_final(struct xattr_iter *it) > > static int init_inode_xattrs(struct inode *inode) > { > + struct erofs_vnode *const vi = EROFS_V(inode); > struct xattr_iter it; > unsigned int i; > struct erofs_xattr_ibody_header *ih; > struct super_block *sb; > struct erofs_sb_info *sbi; > - struct erofs_vnode *vi; > bool atomic_map; > + int ret = 0; > > - if (likely(inode_has_inited_xattr(inode))) > + /* the most case is that xattrs of this inode are initialized. */ > + if (likely(test_bit(EROFS_V_EA_INITED_BIT, &vi->flags))) > return 0; > > - vi = EROFS_V(inode); > + if (wait_on_bit_lock(&vi->flags, EROFS_V_BL_XATTR_BIT, TASK_KILLABLE)) > + return -ERESTARTSYS; > + > + /* someone has initialized xattrs for us? */ > + if (unlikely(test_bit(EROFS_V_EA_INITED_BIT, &vi->flags))) > + goto out_unlock; > > /* > * bypass all xattr operations if ->xattr_isize is not greater than > @@ -68,13 +75,16 @@ static int init_inode_xattrs(struct inode *inode) > if (vi->xattr_isize == sizeof(struct erofs_xattr_ibody_header)) { > errln("xattr_isize %d of nid %llu is not supported yet", > vi->xattr_isize, vi->nid); > - return -ENOTSUPP; > + ret = -ENOTSUPP; > + goto out_unlock; > } else if (vi->xattr_isize < sizeof(struct erofs_xattr_ibody_header)) { > if (unlikely(vi->xattr_isize)) { > DBG_BUGON(1); > - return -EIO; /* xattr ondisk layout error */ > + ret = -EIO; > + goto out_unlock; /* xattr ondisk layout error */ > } > - return -ENOATTR; > + ret = -ENOATTR; > + goto out_unlock; > } > > sb = inode->i_sb; > @@ -83,8 +93,10 @@ static int init_inode_xattrs(struct inode *inode) > it.ofs = erofs_blkoff(iloc(sbi, vi->nid) + vi->inode_isize); > > it.page = erofs_get_inline_page(inode, it.blkaddr); > - if (IS_ERR(it.page)) > - return PTR_ERR(it.page); > + if (IS_ERR(it.page)) { > + ret = PTR_ERR(it.page); > + goto out_unlock; > + } > > /* read in shared xattr array (non-atomic, see kmalloc below) */ > it.kaddr = kmap(it.page); > @@ -97,7 +109,8 @@ static int init_inode_xattrs(struct inode *inode) > sizeof(uint), GFP_KERNEL); > if (vi->xattr_shared_xattrs == NULL) { > xattr_iter_end(&it, atomic_map); > - return -ENOMEM; > + ret = -ENOMEM; > + goto out_unlock; > } > > /* let's skip ibody header */ > @@ -114,7 +127,8 @@ static int init_inode_xattrs(struct inode *inode) > if (IS_ERR(it.page)) { > kfree(vi->xattr_shared_xattrs); > vi->xattr_shared_xattrs = NULL; > - return PTR_ERR(it.page); > + ret = PTR_ERR(it.page); > + goto out_unlock; > } > > it.kaddr = kmap_atomic(it.page); > @@ -127,8 +141,11 @@ static int init_inode_xattrs(struct inode *inode) > } > xattr_iter_end(&it, atomic_map); > > - inode_set_inited_xattr(inode); > - return 0; > + set_bit(EROFS_V_EA_INITED_BIT, &vi->flags); Should it be? set_bit(); wake_up_bit(); return 0; > + > +out_unlock: > + clear_and_wake_up_bit(EROFS_V_BL_XATTR_BIT, &vi->flags); > + return ret; > } > > /* >