]> git.itanic.dy.fi Git - linux-stable/commitdiff
quota: Fix rcu annotations of inode dquot pointers
authorJan Kara <jack@suse.cz>
Tue, 6 Feb 2024 14:32:09 +0000 (15:32 +0100)
committerSasha Levin <sashal@kernel.org>
Tue, 26 Mar 2024 22:17:03 +0000 (18:17 -0400)
[ 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: b9ba6f94b238 ("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>
Signed-off-by: Sasha Levin <sashal@kernel.org>
fs/quota/dquot.c

index cafe65a03f6dcd55d27a3b466eb8ce6c250a490b..3a16867a6fbde0a89880119232ab8d7e6c3d2a69 100644 (file)
@@ -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;
@@ -1000,14 +1000,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))
@@ -1097,14 +1098,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);
                }
@@ -1457,7 +1460,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;
@@ -1532,7 +1536,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
@@ -1540,12 +1544,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);
                        }
                }
@@ -1567,7 +1575,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))
@@ -1592,13 +1600,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);
@@ -1606,7 +1615,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))
@@ -1679,7 +1688,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)) {
@@ -1749,7 +1758,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))
@@ -1794,7 +1803,7 @@ EXPORT_SYMBOL(dquot_alloc_inode);
  */
 void dquot_claim_space_nodirty(struct inode *inode, qsize_t number)
 {
-       struct dquot **dquots;
+       struct dquot __rcu **dquots;
        struct dquot *dquot;
        int cnt, index;
 
@@ -1836,7 +1845,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;
 
@@ -1880,7 +1889,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;
 
@@ -1937,7 +1946,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;
 
@@ -1984,6 +1993,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] = {};
@@ -2016,6 +2026,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.
@@ -2030,7 +2041,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)
@@ -2069,7 +2081,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);
@@ -2080,8 +2092,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);