]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
gfs2: fiemap page fault fix
authorAndreas Gruenbacher <agruenba@redhat.com>
Thu, 5 Feb 2026 14:52:57 +0000 (15:52 +0100)
committerAndreas Gruenbacher <agruenba@redhat.com>
Thu, 5 Feb 2026 17:00:45 +0000 (18:00 +0100)
In gfs2_fiemap(), we are calling iomap_fiemap() while holding the inode
glock.  This can lead to recursive glock taking if the fiemap buffer is
memory mapped to the same inode and accessing it triggers a page fault.

Fix by disabling page faults for iomap_fiemap() and faulting in the
buffer by hand if necessary.

Fixes xfstest generic/742.

Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
fs/gfs2/inode.c

index b6ed069b348721ab8fd5eda377c386535010e8ad..4d65e4a752626cda55c5e7490df4fcfbb14907b2 100644 (file)
@@ -2192,6 +2192,14 @@ static int gfs2_getattr(struct mnt_idmap *idmap,
        return 0;
 }
 
+static bool fault_in_fiemap(struct fiemap_extent_info *fi)
+{
+       struct fiemap_extent __user *dest = fi->fi_extents_start;
+       size_t size = sizeof(*dest) * fi->fi_extents_max;
+
+       return fault_in_safe_writeable((char __user *)dest, size) == 0;
+}
+
 static int gfs2_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
                       u64 start, u64 len)
 {
@@ -2201,14 +2209,22 @@ static int gfs2_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
 
        inode_lock_shared(inode);
 
+retry:
        ret = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, 0, &gh);
        if (ret)
                goto out;
 
+       pagefault_disable();
        ret = iomap_fiemap(inode, fieinfo, start, len, &gfs2_iomap_ops);
+       pagefault_enable();
 
        gfs2_glock_dq_uninit(&gh);
 
+       if (ret == -EFAULT && fault_in_fiemap(fieinfo)) {
+               fieinfo->fi_extents_mapped = 0;
+               goto retry;
+       }
+
 out:
        inode_unlock_shared(inode);
        return ret;