On 2019/2/15 17:09, Gao Xiang wrote: > 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... Yes, you're right, I just missed some points... Reviewed-by: Chao Yu <yuchao0@xxxxxxxxxx> Thanks, > > Thanks, > Gao Xiang > >> >>> + >>> +out_unlock: >>> + clear_and_wake_up_bit(EROFS_V_BL_XATTR_BIT, &vi->flags); >>> + return ret; >>> } >>> >>> /* >>> >> > > . >