gfs2: use i_lock spin_lock for inode qadata
authorBob Peterson <rpeterso@redhat.com>
Fri, 11 Feb 2022 15:50:08 +0000 (10:50 -0500)
committerAndreas Gruenbacher <agruenba@redhat.com>
Tue, 24 May 2022 19:29:14 +0000 (21:29 +0200)
Before this patch, functions gfs2_qa_get and _put used the i_rw_mutex to
prevent simultaneous access to its i_qadata. But i_rw_mutex is now used
for many other things, including iomap_begin and end, which causes a
conflict according to lockdep. We cannot just remove the lock since
simultaneous opens (gfs2_open -> gfs2_open_common -> gfs2_qa_get) can
then stomp on each others values for i_qadata.

This patch solves the conflict by using the i_lock spin_lock in the inode
to prevent simultaneous access.

Signed-off-by: Bob Peterson <rpeterso@redhat.com>
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
fs/gfs2/quota.c

index ec7e091..390f34c 100644 (file)
@@ -528,34 +528,42 @@ static void qdsb_put(struct gfs2_quota_data *qd)
  */
 int gfs2_qa_get(struct gfs2_inode *ip)
 {
-       int error = 0;
        struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
+       struct inode *inode = &ip->i_inode;
 
        if (sdp->sd_args.ar_quota == GFS2_QUOTA_OFF)
                return 0;
 
-       down_write(&ip->i_rw_mutex);
+       spin_lock(&inode->i_lock);
        if (ip->i_qadata == NULL) {
-               ip->i_qadata = kmem_cache_zalloc(gfs2_qadata_cachep, GFP_NOFS);
-               if (!ip->i_qadata) {
-                       error = -ENOMEM;
-                       goto out;
-               }
+               struct gfs2_qadata *tmp;
+
+               spin_unlock(&inode->i_lock);
+               tmp = kmem_cache_zalloc(gfs2_qadata_cachep, GFP_NOFS);
+               if (!tmp)
+                       return -ENOMEM;
+
+               spin_lock(&inode->i_lock);
+               if (ip->i_qadata == NULL)
+                       ip->i_qadata = tmp;
+               else
+                       kmem_cache_free(gfs2_qadata_cachep, tmp);
        }
        ip->i_qadata->qa_ref++;
-out:
-       up_write(&ip->i_rw_mutex);
-       return error;
+       spin_unlock(&inode->i_lock);
+       return 0;
 }
 
 void gfs2_qa_put(struct gfs2_inode *ip)
 {
-       down_write(&ip->i_rw_mutex);
+       struct inode *inode = &ip->i_inode;
+
+       spin_lock(&inode->i_lock);
        if (ip->i_qadata && --ip->i_qadata->qa_ref == 0) {
                kmem_cache_free(gfs2_qadata_cachep, ip->i_qadata);
                ip->i_qadata = NULL;
        }
-       up_write(&ip->i_rw_mutex);
+       spin_unlock(&inode->i_lock);
 }
 
 int gfs2_quota_hold(struct gfs2_inode *ip, kuid_t uid, kgid_t gid)