xfs: pass along transaction context when reading directory block buffers
authorDarrick J. Wong <darrick.wong@oracle.com>
Fri, 16 Jun 2017 18:00:14 +0000 (11:00 -0700)
committerDarrick J. Wong <darrick.wong@oracle.com>
Tue, 20 Jun 2017 17:45:22 +0000 (10:45 -0700)
Teach the directory reading functions to pass along a transaction context
if one was supplied.  The directory scrub code will use transactions to
lock buffers and avoid deadlocking with itself in the case of loops.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: Brian Foster <bfoster@redhat.com>
fs/xfs/libxfs/xfs_dir2_priv.h
fs/xfs/xfs_dir2_readdir.c
fs/xfs/xfs_file.c

index 6d2420974d4f9603e5a3ec5bfd88f2c964388245..4badd26c47e6455c200d7088ff296f261386d3ff 100644 (file)
@@ -130,7 +130,7 @@ extern int xfs_dir2_sf_replace(struct xfs_da_args *args);
 extern int xfs_dir2_sf_verify(struct xfs_inode *ip);
 
 /* xfs_dir2_readdir.c */
-extern int xfs_readdir(struct xfs_inode *dp, struct dir_context *ctx,
-                      size_t bufsize);
+extern int xfs_readdir(struct xfs_trans *tp, struct xfs_inode *dp,
+                      struct dir_context *ctx, size_t bufsize);
 
 #endif /* __XFS_DIR2_PRIV_H__ */
index ede4790753bc1d9def5c2c01beef05386e57eb61..ba2638d370315cad9ed9a5256bc6867c0f44230d 100644 (file)
@@ -170,7 +170,7 @@ xfs_dir2_block_getdents(
                return 0;
 
        lock_mode = xfs_ilock_data_map_shared(dp);
-       error = xfs_dir3_block_read(NULL, dp, &bp);
+       error = xfs_dir3_block_read(args->trans, dp, &bp);
        xfs_iunlock(dp, lock_mode);
        if (error)
                return error;
@@ -228,7 +228,7 @@ xfs_dir2_block_getdents(
                if (!dir_emit(ctx, (char *)dep->name, dep->namelen,
                            be64_to_cpu(dep->inumber),
                            xfs_dir3_get_dtype(dp->i_mount, filetype))) {
-                       xfs_trans_brelse(NULL, bp);
+                       xfs_trans_brelse(args->trans, bp);
                        return 0;
                }
        }
@@ -239,7 +239,7 @@ xfs_dir2_block_getdents(
         */
        ctx->pos = xfs_dir2_db_off_to_dataptr(geo, geo->datablk + 1, 0) &
                                                                0x7fffffff;
-       xfs_trans_brelse(NULL, bp);
+       xfs_trans_brelse(args->trans, bp);
        return 0;
 }
 
@@ -495,15 +495,21 @@ xfs_dir2_leaf_getdents(
        else
                ctx->pos = xfs_dir2_byte_to_dataptr(curoff) & 0x7fffffff;
        if (bp)
-               xfs_trans_brelse(NULL, bp);
+               xfs_trans_brelse(args->trans, bp);
        return error;
 }
 
 /*
  * Read a directory.
+ *
+ * If supplied, the transaction collects locked dir buffers to avoid
+ * nested buffer deadlocks.  This function does not dirty the
+ * transaction.  The caller should ensure that the inode is locked
+ * before calling this function.
  */
 int
 xfs_readdir(
+       struct xfs_trans        *tp,
        struct xfs_inode        *dp,
        struct dir_context      *ctx,
        size_t                  bufsize)
@@ -522,6 +528,7 @@ xfs_readdir(
 
        args.dp = dp;
        args.geo = dp->i_mount->m_dir_geo;
+       args.trans = tp;
 
        if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL)
                rval = xfs_dir2_sf_getdents(&args, ctx);
index 5fb5a0958a1485db46a2f753446c11828ff1913e..36c129303fcfdc0199b750701702b40cc0e2cd94 100644 (file)
@@ -950,7 +950,7 @@ xfs_file_readdir(
         */
        bufsize = (size_t)min_t(loff_t, 32768, ip->i_d.di_size);
 
-       return xfs_readdir(ip, ctx, bufsize);
+       return xfs_readdir(NULL, ip, ctx, bufsize);
 }
 
 /*