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; }
/*
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;
} else if (vi->xattr_isize < sizeof(struct erofs_xattr_ibody_header)) { if (unlikely(vi->xattr_isize)) { DBG_BUGON(1);goto out_unlock;
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;
} /*
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;
} else if (vi->xattr_isize < sizeof(struct erofs_xattr_ibody_header)) { if (unlikely(vi->xattr_isize)) { DBG_BUGON(1);goto out_unlock;
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;
} /*
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;
} else if (vi->xattr_isize < sizeof(struct erofs_xattr_ibody_header)) { if (unlikely(vi->xattr_isize)) { DBG_BUGON(1);goto out_unlock;
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;
} /*
.
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,
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; }
/*
linux-stable-mirror@lists.linaro.org