aboutsummaryrefslogtreecommitdiffstats
path: root/fs/xfs/xfs_bmap.c
diff options
context:
space:
mode:
authorDave Chinner <dchinner@redhat.com>2010-11-29 23:14:39 -0500
committerAlex Elder <aelder@sgi.com>2010-12-01 08:40:19 -0500
commitc726de4409a8d3a03877b1ef4342bfe8a15f5e5e (patch)
treef1212b8f61f6dcdb52206842e8436a45f878a9e9 /fs/xfs/xfs_bmap.c
parente8a7e48bb248a1196484d3f8afa53bded2b24e71 (diff)
xfs: fix failed write truncation handling.
Since the move to the new truncate sequence we call xfs_setattr to truncate down excessively instanciated blocks. As shown by the testcase in kernel.org BZ #22452 that doesn't work too well. Due to the confusion of the internal inode size, and the VFS inode i_size it zeroes data that it shouldn't. But full blown truncate seems like overkill here. We only instanciate delayed allocations in the write path, and given that we never released the iolock we can't have converted them to real allocations yet either. The only nasty case is pre-existing preallocation which we need to skip. We already do this for page discard during writeback, so make the delayed allocation block punching a generic function and call it from the failed write path as well as xfs_aops_discard_page. The callers are responsible for ensuring that partial blocks are not truncated away, and that they hold the ilock. Based on a fix originally from Christoph Hellwig. This version used filesystem blocks as the range unit. Signed-off-by: Dave Chinner <dchinner@redhat.com> Reviewed-by: Christoph Hellwig <hch@lst.de>
Diffstat (limited to 'fs/xfs/xfs_bmap.c')
-rw-r--r--fs/xfs/xfs_bmap.c76
1 files changed, 76 insertions, 0 deletions
diff --git a/fs/xfs/xfs_bmap.c b/fs/xfs/xfs_bmap.c
index 8abd12e32e13..08b179fa9e8f 100644
--- a/fs/xfs/xfs_bmap.c
+++ b/fs/xfs/xfs_bmap.c
@@ -6070,3 +6070,79 @@ xfs_bmap_disk_count_leaves(
6070 *count += xfs_bmbt_disk_get_blockcount(frp); 6070 *count += xfs_bmbt_disk_get_blockcount(frp);
6071 } 6071 }
6072} 6072}
6073
6074/*
6075 * dead simple method of punching delalyed allocation blocks from a range in
6076 * the inode. Walks a block at a time so will be slow, but is only executed in
6077 * rare error cases so the overhead is not critical. This will alays punch out
6078 * both the start and end blocks, even if the ranges only partially overlap
6079 * them, so it is up to the caller to ensure that partial blocks are not
6080 * passed in.
6081 */
6082int
6083xfs_bmap_punch_delalloc_range(
6084 struct xfs_inode *ip,
6085 xfs_fileoff_t start_fsb,
6086 xfs_fileoff_t length)
6087{
6088 xfs_fileoff_t remaining = length;
6089 int error = 0;
6090
6091 ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
6092
6093 do {
6094 int done;
6095 xfs_bmbt_irec_t imap;
6096 int nimaps = 1;
6097 xfs_fsblock_t firstblock;
6098 xfs_bmap_free_t flist;
6099
6100 /*
6101 * Map the range first and check that it is a delalloc extent
6102 * before trying to unmap the range. Otherwise we will be
6103 * trying to remove a real extent (which requires a
6104 * transaction) or a hole, which is probably a bad idea...
6105 */
6106 error = xfs_bmapi(NULL, ip, start_fsb, 1,
6107 XFS_BMAPI_ENTIRE, NULL, 0, &imap,
6108 &nimaps, NULL);
6109
6110 if (error) {
6111 /* something screwed, just bail */
6112 if (!XFS_FORCED_SHUTDOWN(ip->i_mount)) {
6113 xfs_fs_cmn_err(CE_ALERT, ip->i_mount,
6114 "Failed delalloc mapping lookup ino %lld fsb %lld.",
6115 ip->i_ino, start_fsb);
6116 }
6117 break;
6118 }
6119 if (!nimaps) {
6120 /* nothing there */
6121 goto next_block;
6122 }
6123 if (imap.br_startblock != DELAYSTARTBLOCK) {
6124 /* been converted, ignore */
6125 goto next_block;
6126 }
6127 WARN_ON(imap.br_blockcount == 0);
6128
6129 /*
6130 * Note: while we initialise the firstblock/flist pair, they
6131 * should never be used because blocks should never be
6132 * allocated or freed for a delalloc extent and hence we need
6133 * don't cancel or finish them after the xfs_bunmapi() call.
6134 */
6135 xfs_bmap_init(&flist, &firstblock);
6136 error = xfs_bunmapi(NULL, ip, start_fsb, 1, 0, 1, &firstblock,
6137 &flist, &done);
6138 if (error)
6139 break;
6140
6141 ASSERT(!flist.xbf_count && !flist.xbf_first);
6142next_block:
6143 start_fsb++;
6144 remaining--;
6145 } while(remaining > 0);
6146
6147 return error;
6148}