mirror of
https://github.com/torvalds/linux.git
synced 2024-11-13 23:51:39 +00:00
quota: Fix rcu annotations of inode dquot pointers
Dquot pointers in i_dquot array in the inode are protected by
dquot_srcu. Annotate the array pointers with __rcu, perform the locked
dereferences with srcu_dereference_check() instead of plain reads, and
set the array elements with rcu_assign_pointer().
Fixes: b9ba6f94b2
("quota: remove dqptr_sem")
Reported-by: kernel test robot <lkp@intel.com>
Closes: https://lore.kernel.org/oe-kbuild-all/202402061900.rTuYDlo6-lkp@intel.com/
Signed-off-by: Jan Kara <jack@suse.cz>
This commit is contained in:
parent
4243bf80c7
commit
179b8c97eb
@ -399,7 +399,7 @@ int dquot_mark_dquot_dirty(struct dquot *dquot)
|
|||||||
EXPORT_SYMBOL(dquot_mark_dquot_dirty);
|
EXPORT_SYMBOL(dquot_mark_dquot_dirty);
|
||||||
|
|
||||||
/* Dirtify all the dquots - this can block when journalling */
|
/* Dirtify all the dquots - this can block when journalling */
|
||||||
static inline int mark_all_dquot_dirty(struct dquot * const *dquots)
|
static inline int mark_all_dquot_dirty(struct dquot __rcu * const *dquots)
|
||||||
{
|
{
|
||||||
int ret, err, cnt;
|
int ret, err, cnt;
|
||||||
struct dquot *dquot;
|
struct dquot *dquot;
|
||||||
@ -996,14 +996,15 @@ out:
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL(dqget);
|
EXPORT_SYMBOL(dqget);
|
||||||
|
|
||||||
static inline struct dquot **i_dquot(struct inode *inode)
|
static inline struct dquot __rcu **i_dquot(struct inode *inode)
|
||||||
{
|
{
|
||||||
return inode->i_sb->s_op->get_dquots(inode);
|
/* Force __rcu for now until filesystems are fixed */
|
||||||
|
return (struct dquot __rcu **)inode->i_sb->s_op->get_dquots(inode);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dqinit_needed(struct inode *inode, int type)
|
static int dqinit_needed(struct inode *inode, int type)
|
||||||
{
|
{
|
||||||
struct dquot * const *dquots;
|
struct dquot __rcu * const *dquots;
|
||||||
int cnt;
|
int cnt;
|
||||||
|
|
||||||
if (IS_NOQUOTA(inode))
|
if (IS_NOQUOTA(inode))
|
||||||
@ -1093,14 +1094,16 @@ static void remove_dquot_ref(struct super_block *sb, int type)
|
|||||||
*/
|
*/
|
||||||
spin_lock(&dq_data_lock);
|
spin_lock(&dq_data_lock);
|
||||||
if (!IS_NOQUOTA(inode)) {
|
if (!IS_NOQUOTA(inode)) {
|
||||||
struct dquot **dquots = i_dquot(inode);
|
struct dquot __rcu **dquots = i_dquot(inode);
|
||||||
struct dquot *dquot = dquots[type];
|
struct dquot *dquot = srcu_dereference_check(
|
||||||
|
dquots[type], &dquot_srcu,
|
||||||
|
lockdep_is_held(&dq_data_lock));
|
||||||
|
|
||||||
#ifdef CONFIG_QUOTA_DEBUG
|
#ifdef CONFIG_QUOTA_DEBUG
|
||||||
if (unlikely(inode_get_rsv_space(inode) > 0))
|
if (unlikely(inode_get_rsv_space(inode) > 0))
|
||||||
reserved = 1;
|
reserved = 1;
|
||||||
#endif
|
#endif
|
||||||
dquots[type] = NULL;
|
rcu_assign_pointer(dquots[type], NULL);
|
||||||
if (dquot)
|
if (dquot)
|
||||||
dqput(dquot);
|
dqput(dquot);
|
||||||
}
|
}
|
||||||
@ -1453,7 +1456,8 @@ static int inode_quota_active(const struct inode *inode)
|
|||||||
static int __dquot_initialize(struct inode *inode, int type)
|
static int __dquot_initialize(struct inode *inode, int type)
|
||||||
{
|
{
|
||||||
int cnt, init_needed = 0;
|
int cnt, init_needed = 0;
|
||||||
struct dquot **dquots, *got[MAXQUOTAS] = {};
|
struct dquot __rcu **dquots;
|
||||||
|
struct dquot *got[MAXQUOTAS] = {};
|
||||||
struct super_block *sb = inode->i_sb;
|
struct super_block *sb = inode->i_sb;
|
||||||
qsize_t rsv;
|
qsize_t rsv;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
@ -1528,7 +1532,7 @@ static int __dquot_initialize(struct inode *inode, int type)
|
|||||||
if (!got[cnt])
|
if (!got[cnt])
|
||||||
continue;
|
continue;
|
||||||
if (!dquots[cnt]) {
|
if (!dquots[cnt]) {
|
||||||
dquots[cnt] = got[cnt];
|
rcu_assign_pointer(dquots[cnt], got[cnt]);
|
||||||
got[cnt] = NULL;
|
got[cnt] = NULL;
|
||||||
/*
|
/*
|
||||||
* Make quota reservation system happy if someone
|
* Make quota reservation system happy if someone
|
||||||
@ -1536,12 +1540,16 @@ static int __dquot_initialize(struct inode *inode, int type)
|
|||||||
*/
|
*/
|
||||||
rsv = inode_get_rsv_space(inode);
|
rsv = inode_get_rsv_space(inode);
|
||||||
if (unlikely(rsv)) {
|
if (unlikely(rsv)) {
|
||||||
|
struct dquot *dquot = srcu_dereference_check(
|
||||||
|
dquots[cnt], &dquot_srcu,
|
||||||
|
lockdep_is_held(&dq_data_lock));
|
||||||
|
|
||||||
spin_lock(&inode->i_lock);
|
spin_lock(&inode->i_lock);
|
||||||
/* Get reservation again under proper lock */
|
/* Get reservation again under proper lock */
|
||||||
rsv = __inode_get_rsv_space(inode);
|
rsv = __inode_get_rsv_space(inode);
|
||||||
spin_lock(&dquots[cnt]->dq_dqb_lock);
|
spin_lock(&dquot->dq_dqb_lock);
|
||||||
dquots[cnt]->dq_dqb.dqb_rsvspace += rsv;
|
dquot->dq_dqb.dqb_rsvspace += rsv;
|
||||||
spin_unlock(&dquots[cnt]->dq_dqb_lock);
|
spin_unlock(&dquot->dq_dqb_lock);
|
||||||
spin_unlock(&inode->i_lock);
|
spin_unlock(&inode->i_lock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1563,7 +1571,7 @@ EXPORT_SYMBOL(dquot_initialize);
|
|||||||
|
|
||||||
bool dquot_initialize_needed(struct inode *inode)
|
bool dquot_initialize_needed(struct inode *inode)
|
||||||
{
|
{
|
||||||
struct dquot **dquots;
|
struct dquot __rcu **dquots;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (!inode_quota_active(inode))
|
if (!inode_quota_active(inode))
|
||||||
@ -1588,13 +1596,14 @@ EXPORT_SYMBOL(dquot_initialize_needed);
|
|||||||
static void __dquot_drop(struct inode *inode)
|
static void __dquot_drop(struct inode *inode)
|
||||||
{
|
{
|
||||||
int cnt;
|
int cnt;
|
||||||
struct dquot **dquots = i_dquot(inode);
|
struct dquot __rcu **dquots = i_dquot(inode);
|
||||||
struct dquot *put[MAXQUOTAS];
|
struct dquot *put[MAXQUOTAS];
|
||||||
|
|
||||||
spin_lock(&dq_data_lock);
|
spin_lock(&dq_data_lock);
|
||||||
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
||||||
put[cnt] = dquots[cnt];
|
put[cnt] = srcu_dereference_check(dquots[cnt], &dquot_srcu,
|
||||||
dquots[cnt] = NULL;
|
lockdep_is_held(&dq_data_lock));
|
||||||
|
rcu_assign_pointer(dquots[cnt], NULL);
|
||||||
}
|
}
|
||||||
spin_unlock(&dq_data_lock);
|
spin_unlock(&dq_data_lock);
|
||||||
dqput_all(put);
|
dqput_all(put);
|
||||||
@ -1602,7 +1611,7 @@ static void __dquot_drop(struct inode *inode)
|
|||||||
|
|
||||||
void dquot_drop(struct inode *inode)
|
void dquot_drop(struct inode *inode)
|
||||||
{
|
{
|
||||||
struct dquot * const *dquots;
|
struct dquot __rcu * const *dquots;
|
||||||
int cnt;
|
int cnt;
|
||||||
|
|
||||||
if (IS_NOQUOTA(inode))
|
if (IS_NOQUOTA(inode))
|
||||||
@ -1675,7 +1684,7 @@ int __dquot_alloc_space(struct inode *inode, qsize_t number, int flags)
|
|||||||
int cnt, ret = 0, index;
|
int cnt, ret = 0, index;
|
||||||
struct dquot_warn warn[MAXQUOTAS];
|
struct dquot_warn warn[MAXQUOTAS];
|
||||||
int reserve = flags & DQUOT_SPACE_RESERVE;
|
int reserve = flags & DQUOT_SPACE_RESERVE;
|
||||||
struct dquot **dquots;
|
struct dquot __rcu **dquots;
|
||||||
struct dquot *dquot;
|
struct dquot *dquot;
|
||||||
|
|
||||||
if (!inode_quota_active(inode)) {
|
if (!inode_quota_active(inode)) {
|
||||||
@ -1745,7 +1754,7 @@ int dquot_alloc_inode(struct inode *inode)
|
|||||||
{
|
{
|
||||||
int cnt, ret = 0, index;
|
int cnt, ret = 0, index;
|
||||||
struct dquot_warn warn[MAXQUOTAS];
|
struct dquot_warn warn[MAXQUOTAS];
|
||||||
struct dquot * const *dquots;
|
struct dquot __rcu * const *dquots;
|
||||||
struct dquot *dquot;
|
struct dquot *dquot;
|
||||||
|
|
||||||
if (!inode_quota_active(inode))
|
if (!inode_quota_active(inode))
|
||||||
@ -1790,7 +1799,7 @@ EXPORT_SYMBOL(dquot_alloc_inode);
|
|||||||
*/
|
*/
|
||||||
void dquot_claim_space_nodirty(struct inode *inode, qsize_t number)
|
void dquot_claim_space_nodirty(struct inode *inode, qsize_t number)
|
||||||
{
|
{
|
||||||
struct dquot **dquots;
|
struct dquot __rcu **dquots;
|
||||||
struct dquot *dquot;
|
struct dquot *dquot;
|
||||||
int cnt, index;
|
int cnt, index;
|
||||||
|
|
||||||
@ -1832,7 +1841,7 @@ EXPORT_SYMBOL(dquot_claim_space_nodirty);
|
|||||||
*/
|
*/
|
||||||
void dquot_reclaim_space_nodirty(struct inode *inode, qsize_t number)
|
void dquot_reclaim_space_nodirty(struct inode *inode, qsize_t number)
|
||||||
{
|
{
|
||||||
struct dquot **dquots;
|
struct dquot __rcu **dquots;
|
||||||
struct dquot *dquot;
|
struct dquot *dquot;
|
||||||
int cnt, index;
|
int cnt, index;
|
||||||
|
|
||||||
@ -1876,7 +1885,7 @@ void __dquot_free_space(struct inode *inode, qsize_t number, int flags)
|
|||||||
{
|
{
|
||||||
unsigned int cnt;
|
unsigned int cnt;
|
||||||
struct dquot_warn warn[MAXQUOTAS];
|
struct dquot_warn warn[MAXQUOTAS];
|
||||||
struct dquot **dquots;
|
struct dquot __rcu **dquots;
|
||||||
struct dquot *dquot;
|
struct dquot *dquot;
|
||||||
int reserve = flags & DQUOT_SPACE_RESERVE, index;
|
int reserve = flags & DQUOT_SPACE_RESERVE, index;
|
||||||
|
|
||||||
@ -1933,7 +1942,7 @@ void dquot_free_inode(struct inode *inode)
|
|||||||
{
|
{
|
||||||
unsigned int cnt;
|
unsigned int cnt;
|
||||||
struct dquot_warn warn[MAXQUOTAS];
|
struct dquot_warn warn[MAXQUOTAS];
|
||||||
struct dquot * const *dquots;
|
struct dquot __rcu * const *dquots;
|
||||||
struct dquot *dquot;
|
struct dquot *dquot;
|
||||||
int index;
|
int index;
|
||||||
|
|
||||||
@ -1980,6 +1989,7 @@ int __dquot_transfer(struct inode *inode, struct dquot **transfer_to)
|
|||||||
qsize_t cur_space;
|
qsize_t cur_space;
|
||||||
qsize_t rsv_space = 0;
|
qsize_t rsv_space = 0;
|
||||||
qsize_t inode_usage = 1;
|
qsize_t inode_usage = 1;
|
||||||
|
struct dquot __rcu **dquots;
|
||||||
struct dquot *transfer_from[MAXQUOTAS] = {};
|
struct dquot *transfer_from[MAXQUOTAS] = {};
|
||||||
int cnt, index, ret = 0;
|
int cnt, index, ret = 0;
|
||||||
char is_valid[MAXQUOTAS] = {};
|
char is_valid[MAXQUOTAS] = {};
|
||||||
@ -2012,6 +2022,7 @@ int __dquot_transfer(struct inode *inode, struct dquot **transfer_to)
|
|||||||
}
|
}
|
||||||
cur_space = __inode_get_bytes(inode);
|
cur_space = __inode_get_bytes(inode);
|
||||||
rsv_space = __inode_get_rsv_space(inode);
|
rsv_space = __inode_get_rsv_space(inode);
|
||||||
|
dquots = i_dquot(inode);
|
||||||
/*
|
/*
|
||||||
* Build the transfer_from list, check limits, and update usage in
|
* Build the transfer_from list, check limits, and update usage in
|
||||||
* the target structures.
|
* the target structures.
|
||||||
@ -2026,7 +2037,8 @@ int __dquot_transfer(struct inode *inode, struct dquot **transfer_to)
|
|||||||
if (!sb_has_quota_active(inode->i_sb, cnt))
|
if (!sb_has_quota_active(inode->i_sb, cnt))
|
||||||
continue;
|
continue;
|
||||||
is_valid[cnt] = 1;
|
is_valid[cnt] = 1;
|
||||||
transfer_from[cnt] = i_dquot(inode)[cnt];
|
transfer_from[cnt] = srcu_dereference_check(dquots[cnt],
|
||||||
|
&dquot_srcu, lockdep_is_held(&dq_data_lock));
|
||||||
ret = dquot_add_inodes(transfer_to[cnt], inode_usage,
|
ret = dquot_add_inodes(transfer_to[cnt], inode_usage,
|
||||||
&warn_to[cnt]);
|
&warn_to[cnt]);
|
||||||
if (ret)
|
if (ret)
|
||||||
@ -2065,7 +2077,7 @@ int __dquot_transfer(struct inode *inode, struct dquot **transfer_to)
|
|||||||
rsv_space);
|
rsv_space);
|
||||||
spin_unlock(&transfer_from[cnt]->dq_dqb_lock);
|
spin_unlock(&transfer_from[cnt]->dq_dqb_lock);
|
||||||
}
|
}
|
||||||
i_dquot(inode)[cnt] = transfer_to[cnt];
|
rcu_assign_pointer(dquots[cnt], transfer_to[cnt]);
|
||||||
}
|
}
|
||||||
spin_unlock(&inode->i_lock);
|
spin_unlock(&inode->i_lock);
|
||||||
spin_unlock(&dq_data_lock);
|
spin_unlock(&dq_data_lock);
|
||||||
@ -2076,8 +2088,8 @@ int __dquot_transfer(struct inode *inode, struct dquot **transfer_to)
|
|||||||
* mark_all_dquot_dirty().
|
* mark_all_dquot_dirty().
|
||||||
*/
|
*/
|
||||||
index = srcu_read_lock(&dquot_srcu);
|
index = srcu_read_lock(&dquot_srcu);
|
||||||
mark_all_dquot_dirty(transfer_from);
|
mark_all_dquot_dirty((struct dquot __rcu **)transfer_from);
|
||||||
mark_all_dquot_dirty(transfer_to);
|
mark_all_dquot_dirty((struct dquot __rcu **)transfer_to);
|
||||||
srcu_read_unlock(&dquot_srcu, index);
|
srcu_read_unlock(&dquot_srcu, index);
|
||||||
|
|
||||||
flush_warnings(warn_to);
|
flush_warnings(warn_to);
|
||||||
|
Loading…
Reference in New Issue
Block a user