Hi Chao, On 2019/2/15 15:29, Chao Yu wrote: > 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; set_bit(EROFS_V_EA_INITED_BIT, &vi->flags); <- set xattrs initialized clear_and_wake_up_bit(EROFS_V_BL_XATTR_BIT, &vi->flags); <- clear EROFS_V_BL_XATTR_BIT bitlock and wake up waiting tasks... Thanks, Gao Xiang > >> + >> +out_unlock: >> + clear_and_wake_up_bit(EROFS_V_BL_XATTR_BIT, &vi->flags); >> + return ret; >> } >> >> /* >> >