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: <[email protected]> # 4.19+
>> Signed-off-by: Gao Xiang <[email protected]>
>> ---
>>  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;
>>  }
>>  
>>  /*
>>
> 

Reply via email to