* [PREVIEW] [PATCH] staging: erofs: fix race of initializing xattrs of a inode at the same time
@ 2019-02-14 15:34 Gao Xiang
2019-02-15 7:29 ` Chao Yu
2019-02-18 7:19 ` Gao Xiang
0 siblings, 2 replies; 6+ messages in thread
From: Gao Xiang @ 2019-02-14 15:34 UTC (permalink / raw)
To: linux-erofs, Chao Yu
Cc: Chao Yu, Miao Xie, weidu.du, Fang Wei, Gao Xiang, stable
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.
That's actually an unexpected behavior, this patch closes the race.
Fixes: b17500a0fdba ("staging: erofs: introduce xattr & acl support")
Cc: <stable@vger.kernel.org> # 4.19+
Signed-off-by: Gao Xiang <gaoxiang25@huawei.com>
---
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);
+
+out_unlock:
+ clear_and_wake_up_bit(EROFS_V_BL_XATTR_BIT, &vi->flags);
+ return ret;
}
/*
--
2.14.4
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PREVIEW] [PATCH] staging: erofs: fix race of initializing xattrs of a inode at the same time
2019-02-14 15:34 [PREVIEW] [PATCH] staging: erofs: fix race of initializing xattrs of a inode at the same time Gao Xiang
@ 2019-02-15 7:29 ` Chao Yu
2019-02-15 9:09 ` Gao Xiang
2019-02-18 7:19 ` Gao Xiang
1 sibling, 1 reply; 6+ messages in thread
From: Chao Yu @ 2019-02-15 7:29 UTC (permalink / raw)
To: Gao Xiang, linux-erofs, Chao Yu; +Cc: Miao Xie, weidu.du, Fang Wei, stable
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@vger.kernel.org> # 4.19+
> Signed-off-by: Gao Xiang <gaoxiang25@huawei.com>
> ---
> 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;
> }
>
> /*
>
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PREVIEW] [PATCH] staging: erofs: fix race of initializing xattrs of a inode at the same time
2019-02-15 7:29 ` Chao Yu
@ 2019-02-15 9:09 ` Gao Xiang
2019-02-18 1:43 ` Chao Yu
0 siblings, 1 reply; 6+ messages in thread
From: Gao Xiang @ 2019-02-15 9:09 UTC (permalink / raw)
To: Chao Yu; +Cc: linux-erofs, Chao Yu, Miao Xie, weidu.du, Fang Wei, stable
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@vger.kernel.org> # 4.19+
>> Signed-off-by: Gao Xiang <gaoxiang25@huawei.com>
>> ---
>> 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;
>> }
>>
>> /*
>>
>
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PREVIEW] [PATCH] staging: erofs: fix race of initializing xattrs of a inode at the same time
2019-02-15 9:09 ` Gao Xiang
@ 2019-02-18 1:43 ` Chao Yu
2019-02-18 2:23 ` Gao Xiang
0 siblings, 1 reply; 6+ messages in thread
From: Chao Yu @ 2019-02-18 1:43 UTC (permalink / raw)
To: Gao Xiang; +Cc: linux-erofs, Chao Yu, Miao Xie, weidu.du, Fang Wei, stable
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@vger.kernel.org> # 4.19+
>>> Signed-off-by: Gao Xiang <gaoxiang25@huawei.com>
>>> ---
>>> 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@huawei.com>
Thanks,
>
> Thanks,
> Gao Xiang
>
>>
>>> +
>>> +out_unlock:
>>> + clear_and_wake_up_bit(EROFS_V_BL_XATTR_BIT, &vi->flags);
>>> + return ret;
>>> }
>>>
>>> /*
>>>
>>
>
> .
>
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PREVIEW] [PATCH] staging: erofs: fix race of initializing xattrs of a inode at the same time
2019-02-18 1:43 ` Chao Yu
@ 2019-02-18 2:23 ` Gao Xiang
0 siblings, 0 replies; 6+ messages in thread
From: Gao Xiang @ 2019-02-18 2:23 UTC (permalink / raw)
To: Chao Yu; +Cc: linux-erofs, Chao Yu, Miao Xie, weidu.du, Fang Wei, stable
Hi Chao,
On 2019/2/18 9:43, Chao Yu wrote:
> Yes, you're right, I just missed some points...
>
> Reviewed-by: Chao Yu <yuchao0@huawei.com>
>
Thanks for kindly reviewing, I can send this patch to the staging list later. :)
Thanks,
Gao Xiang
> Thanks,
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH] staging: erofs: fix race of initializing xattrs of a inode at the same time
2019-02-14 15:34 [PREVIEW] [PATCH] staging: erofs: fix race of initializing xattrs of a inode at the same time Gao Xiang
2019-02-15 7:29 ` Chao Yu
@ 2019-02-18 7:19 ` Gao Xiang
1 sibling, 0 replies; 6+ messages in thread
From: Gao Xiang @ 2019-02-18 7:19 UTC (permalink / raw)
To: Greg Kroah-Hartman, Chao Yu, devel
Cc: LKML, linux-erofs, Chao Yu, Miao Xie, weidu.du, Fang Wei,
Gao Xiang, stable
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.
That's actually an unexpected behavior, this patch closes the race.
Fixes: b17500a0fdba ("staging: erofs: introduce xattr & acl support")
Cc: <stable@vger.kernel.org> # 4.19+
Reviewed-by: Chao Yu <yuchao0@huawei.com>
Signed-off-by: Gao Xiang <gaoxiang25@huawei.com>
---
[previewed at https://lists.ozlabs.org/pipermail/linux-erofs/2019-February/001345.html]
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..f716ab0446e5 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 (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 (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);
+
+out_unlock:
+ clear_and_wake_up_bit(EROFS_V_BL_XATTR_BIT, &vi->flags);
+ return ret;
}
/*
--
2.14.4
^ permalink raw reply related [flat|nested] 6+ messages in thread
end of thread, other threads:[~2019-02-18 7:20 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2019-02-14 15:34 [PREVIEW] [PATCH] staging: erofs: fix race of initializing xattrs of a inode at the same time Gao Xiang
2019-02-15 7:29 ` Chao Yu
2019-02-15 9:09 ` Gao Xiang
2019-02-18 1:43 ` Chao Yu
2019-02-18 2:23 ` Gao Xiang
2019-02-18 7:19 ` Gao Xiang
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).