Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 00b07b49 authored by Jan Kara's avatar Jan Kara Committed by Sasha Levin
Browse files

quota: Fix rcu annotations of inode dquot pointers



[ Upstream commit 179b8c97ebf63429589f5afeba59a181fe70603e ]

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: b9ba6f94 ("quota: remove dqptr_sem")
Reported-by: default avatarkernel test robot <lkp@intel.com>
Closes: https://lore.kernel.org/oe-kbuild-all/202402061900.rTuYDlo6-lkp@intel.com/


Signed-off-by: default avatarJan Kara <jack@suse.cz>
Signed-off-by: default avatarSasha Levin <sashal@kernel.org>
parent 49669f8e
Loading
Loading
Loading
Loading
+39 −27
Original line number Diff line number Diff line
@@ -399,7 +399,7 @@ int dquot_mark_dquot_dirty(struct dquot *dquot)
EXPORT_SYMBOL(dquot_mark_dquot_dirty);

/* 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;
	struct dquot *dquot;
@@ -997,14 +997,15 @@ struct dquot *dqget(struct super_block *sb, struct kqid qid)
}
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)
{
	struct dquot * const *dquots;
	struct dquot __rcu * const *dquots;
	int cnt;

	if (IS_NOQUOTA(inode))
@@ -1094,14 +1095,16 @@ static void remove_dquot_ref(struct super_block *sb, int type)
		 */
		spin_lock(&dq_data_lock);
		if (!IS_NOQUOTA(inode)) {
			struct dquot **dquots = i_dquot(inode);
			struct dquot *dquot = dquots[type];
			struct dquot __rcu **dquots = i_dquot(inode);
			struct dquot *dquot = srcu_dereference_check(
				dquots[type], &dquot_srcu,
				lockdep_is_held(&dq_data_lock));

#ifdef CONFIG_QUOTA_DEBUG
			if (unlikely(inode_get_rsv_space(inode) > 0))
				reserved = 1;
#endif
			dquots[type] = NULL;
			rcu_assign_pointer(dquots[type], NULL);
			if (dquot)
				dqput(dquot);
		}
@@ -1454,7 +1457,8 @@ static int inode_quota_active(const struct inode *inode)
static int __dquot_initialize(struct inode *inode, int type)
{
	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;
	qsize_t rsv;
	int ret = 0;
@@ -1529,7 +1533,7 @@ static int __dquot_initialize(struct inode *inode, int type)
		if (!got[cnt])
			continue;
		if (!dquots[cnt]) {
			dquots[cnt] = got[cnt];
			rcu_assign_pointer(dquots[cnt], got[cnt]);
			got[cnt] = NULL;
			/*
			 * Make quota reservation system happy if someone
@@ -1537,12 +1541,16 @@ static int __dquot_initialize(struct inode *inode, int type)
			 */
			rsv = inode_get_rsv_space(inode);
			if (unlikely(rsv)) {
				struct dquot *dquot = srcu_dereference_check(
					dquots[cnt], &dquot_srcu,
					lockdep_is_held(&dq_data_lock));

				spin_lock(&inode->i_lock);
				/* Get reservation again under proper lock */
				rsv = __inode_get_rsv_space(inode);
				spin_lock(&dquots[cnt]->dq_dqb_lock);
				dquots[cnt]->dq_dqb.dqb_rsvspace += rsv;
				spin_unlock(&dquots[cnt]->dq_dqb_lock);
				spin_lock(&dquot->dq_dqb_lock);
				dquot->dq_dqb.dqb_rsvspace += rsv;
				spin_unlock(&dquot->dq_dqb_lock);
				spin_unlock(&inode->i_lock);
			}
		}
@@ -1564,7 +1572,7 @@ EXPORT_SYMBOL(dquot_initialize);

bool dquot_initialize_needed(struct inode *inode)
{
	struct dquot **dquots;
	struct dquot __rcu **dquots;
	int i;

	if (!inode_quota_active(inode))
@@ -1589,13 +1597,14 @@ EXPORT_SYMBOL(dquot_initialize_needed);
static void __dquot_drop(struct inode *inode)
{
	int cnt;
	struct dquot **dquots = i_dquot(inode);
	struct dquot __rcu **dquots = i_dquot(inode);
	struct dquot *put[MAXQUOTAS];

	spin_lock(&dq_data_lock);
	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
		put[cnt] = dquots[cnt];
		dquots[cnt] = NULL;
		put[cnt] = srcu_dereference_check(dquots[cnt], &dquot_srcu,
					lockdep_is_held(&dq_data_lock));
		rcu_assign_pointer(dquots[cnt], NULL);
	}
	spin_unlock(&dq_data_lock);
	dqput_all(put);
@@ -1603,7 +1612,7 @@ static void __dquot_drop(struct inode *inode)

void dquot_drop(struct inode *inode)
{
	struct dquot * const *dquots;
	struct dquot __rcu * const *dquots;
	int cnt;

	if (IS_NOQUOTA(inode))
@@ -1676,7 +1685,7 @@ int __dquot_alloc_space(struct inode *inode, qsize_t number, int flags)
	int cnt, ret = 0, index;
	struct dquot_warn warn[MAXQUOTAS];
	int reserve = flags & DQUOT_SPACE_RESERVE;
	struct dquot **dquots;
	struct dquot __rcu **dquots;
	struct dquot *dquot;

	if (!inode_quota_active(inode)) {
@@ -1746,7 +1755,7 @@ int dquot_alloc_inode(struct inode *inode)
{
	int cnt, ret = 0, index;
	struct dquot_warn warn[MAXQUOTAS];
	struct dquot * const *dquots;
	struct dquot __rcu * const *dquots;
	struct dquot *dquot;

	if (!inode_quota_active(inode))
@@ -1791,7 +1800,7 @@ EXPORT_SYMBOL(dquot_alloc_inode);
 */
int dquot_claim_space_nodirty(struct inode *inode, qsize_t number)
{
	struct dquot **dquots;
	struct dquot __rcu **dquots;
	struct dquot *dquot;
	int cnt, index;

@@ -1833,7 +1842,7 @@ EXPORT_SYMBOL(dquot_claim_space_nodirty);
 */
void dquot_reclaim_space_nodirty(struct inode *inode, qsize_t number)
{
	struct dquot **dquots;
	struct dquot __rcu **dquots;
	struct dquot *dquot;
	int cnt, index;

@@ -1877,7 +1886,7 @@ void __dquot_free_space(struct inode *inode, qsize_t number, int flags)
{
	unsigned int cnt;
	struct dquot_warn warn[MAXQUOTAS];
	struct dquot **dquots;
	struct dquot __rcu **dquots;
	struct dquot *dquot;
	int reserve = flags & DQUOT_SPACE_RESERVE, index;

@@ -1934,7 +1943,7 @@ void dquot_free_inode(struct inode *inode)
{
	unsigned int cnt;
	struct dquot_warn warn[MAXQUOTAS];
	struct dquot * const *dquots;
	struct dquot __rcu * const *dquots;
	struct dquot *dquot;
	int index;

@@ -1981,6 +1990,7 @@ int __dquot_transfer(struct inode *inode, struct dquot **transfer_to)
	qsize_t cur_space;
	qsize_t rsv_space = 0;
	qsize_t inode_usage = 1;
	struct dquot __rcu **dquots;
	struct dquot *transfer_from[MAXQUOTAS] = {};
	int cnt, index, ret = 0;
	char is_valid[MAXQUOTAS] = {};
@@ -2013,6 +2023,7 @@ int __dquot_transfer(struct inode *inode, struct dquot **transfer_to)
	}
	cur_space = __inode_get_bytes(inode);
	rsv_space = __inode_get_rsv_space(inode);
	dquots = i_dquot(inode);
	/*
	 * Build the transfer_from list, check limits, and update usage in
	 * the target structures.
@@ -2027,7 +2038,8 @@ int __dquot_transfer(struct inode *inode, struct dquot **transfer_to)
		if (!sb_has_quota_active(inode->i_sb, cnt))
			continue;
		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,
				       &warn_to[cnt]);
		if (ret)
@@ -2066,7 +2078,7 @@ int __dquot_transfer(struct inode *inode, struct dquot **transfer_to)
						  rsv_space);
			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(&dq_data_lock);
@@ -2077,8 +2089,8 @@ int __dquot_transfer(struct inode *inode, struct dquot **transfer_to)
	 * mark_all_dquot_dirty().
	 */
	index = srcu_read_lock(&dquot_srcu);
	mark_all_dquot_dirty(transfer_from);
	mark_all_dquot_dirty(transfer_to);
	mark_all_dquot_dirty((struct dquot __rcu **)transfer_from);
	mark_all_dquot_dirty((struct dquot __rcu **)transfer_to);
	srcu_read_unlock(&dquot_srcu, index);

	flush_warnings(warn_to);