aboutsummaryrefslogtreecommitdiffstats
path: root/fs/xfs/xfs_vnodeops.c
diff options
context:
space:
mode:
authorDave Chinner <dchinner@redhat.com>2012-11-28 23:26:33 -0500
committerBen Myers <bpm@sgi.com>2012-11-29 15:21:46 -0500
commitef9d873344ff9f5084eacb9f3735982314dfda9e (patch)
tree5ef54dd33ac54de24239fecf36c0c960f6f70972 /fs/xfs/xfs_vnodeops.c
parent7c4cebe8e02dd0b0e655605442bbe9268db9ed4f (diff)
xfs: byte range granularity for XFS_IOC_ZERO_RANGE
XFS_IOC_ZERO_RANGE simply does not work properly for non page cache aligned ranges. Neither test 242 or 290 exercise this correctly, so the behaviour is completely busted even though the tests pass. Fix it to support full byte range granularity as was originally intended for this ioctl. Signed-off-by: Dave Chinner <dchinner@redhat.com> Reviewed-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Ben Myers <bpm@sgi.com>
Diffstat (limited to 'fs/xfs/xfs_vnodeops.c')
-rw-r--r--fs/xfs/xfs_vnodeops.c96
1 files changed, 75 insertions, 21 deletions
diff --git a/fs/xfs/xfs_vnodeops.c b/fs/xfs/xfs_vnodeops.c
index 26880793feca..d95f565a390e 100644
--- a/fs/xfs/xfs_vnodeops.c
+++ b/fs/xfs/xfs_vnodeops.c
@@ -2095,6 +2095,73 @@ xfs_free_file_space(
2095 return error; 2095 return error;
2096} 2096}
2097 2097
2098
2099STATIC int
2100xfs_zero_file_space(
2101 struct xfs_inode *ip,
2102 xfs_off_t offset,
2103 xfs_off_t len,
2104 int attr_flags)
2105{
2106 struct xfs_mount *mp = ip->i_mount;
2107 uint granularity;
2108 xfs_off_t start_boundary;
2109 xfs_off_t end_boundary;
2110 int error;
2111
2112 granularity = max_t(uint, 1 << mp->m_sb.sb_blocklog, PAGE_CACHE_SIZE);
2113
2114 /*
2115 * Round the range of extents we are going to convert inwards. If the
2116 * offset is aligned, then it doesn't get changed so we zero from the
2117 * start of the block offset points to.
2118 */
2119 start_boundary = round_up(offset, granularity);
2120 end_boundary = round_down(offset + len, granularity);
2121
2122 ASSERT(start_boundary >= offset);
2123 ASSERT(end_boundary <= offset + len);
2124
2125 if (!(attr_flags & XFS_ATTR_NOLOCK))
2126 xfs_ilock(ip, XFS_IOLOCK_EXCL);
2127
2128 if (start_boundary < end_boundary - 1) {
2129 /* punch out the page cache over the conversion range */
2130 truncate_pagecache_range(VFS_I(ip), start_boundary,
2131 end_boundary - 1);
2132 /* convert the blocks */
2133 error = xfs_alloc_file_space(ip, start_boundary,
2134 end_boundary - start_boundary - 1,
2135 XFS_BMAPI_PREALLOC | XFS_BMAPI_CONVERT,
2136 attr_flags);
2137 if (error)
2138 goto out_unlock;
2139
2140 /* We've handled the interior of the range, now for the edges */
2141 if (start_boundary != offset)
2142 error = xfs_iozero(ip, offset, start_boundary - offset);
2143 if (error)
2144 goto out_unlock;
2145
2146 if (end_boundary != offset + len)
2147 error = xfs_iozero(ip, end_boundary,
2148 offset + len - end_boundary);
2149
2150 } else {
2151 /*
2152 * It's either a sub-granularity range or the range spanned lies
2153 * partially across two adjacent blocks.
2154 */
2155 error = xfs_iozero(ip, offset, len);
2156 }
2157
2158out_unlock:
2159 if (!(attr_flags & XFS_ATTR_NOLOCK))
2160 xfs_iunlock(ip, XFS_IOLOCK_EXCL);
2161 return error;
2162
2163}
2164
2098/* 2165/*
2099 * xfs_change_file_space() 2166 * xfs_change_file_space()
2100 * This routine allocates or frees disk space for the given file. 2167 * This routine allocates or frees disk space for the given file.
@@ -2120,10 +2187,8 @@ xfs_change_file_space(
2120 xfs_fsize_t fsize; 2187 xfs_fsize_t fsize;
2121 int setprealloc; 2188 int setprealloc;
2122 xfs_off_t startoffset; 2189 xfs_off_t startoffset;
2123 xfs_off_t end;
2124 xfs_trans_t *tp; 2190 xfs_trans_t *tp;
2125 struct iattr iattr; 2191 struct iattr iattr;
2126 int prealloc_type;
2127 2192
2128 if (!S_ISREG(ip->i_d.di_mode)) 2193 if (!S_ISREG(ip->i_d.di_mode))
2129 return XFS_ERROR(EINVAL); 2194 return XFS_ERROR(EINVAL);
@@ -2172,31 +2237,20 @@ xfs_change_file_space(
2172 startoffset = bf->l_start; 2237 startoffset = bf->l_start;
2173 fsize = XFS_ISIZE(ip); 2238 fsize = XFS_ISIZE(ip);
2174 2239
2175 /*
2176 * XFS_IOC_RESVSP and XFS_IOC_UNRESVSP will reserve or unreserve
2177 * file space.
2178 * These calls do NOT zero the data space allocated to the file,
2179 * nor do they change the file size.
2180 *
2181 * XFS_IOC_ALLOCSP and XFS_IOC_FREESP will allocate and free file
2182 * space.
2183 * These calls cause the new file data to be zeroed and the file
2184 * size to be changed.
2185 */
2186 setprealloc = clrprealloc = 0; 2240 setprealloc = clrprealloc = 0;
2187 prealloc_type = XFS_BMAPI_PREALLOC;
2188
2189 switch (cmd) { 2241 switch (cmd) {
2190 case XFS_IOC_ZERO_RANGE: 2242 case XFS_IOC_ZERO_RANGE:
2191 prealloc_type |= XFS_BMAPI_CONVERT; 2243 error = xfs_zero_file_space(ip, startoffset, bf->l_len,
2192 end = round_down(startoffset + bf->l_len, PAGE_SIZE) - 1; 2244 attr_flags);
2193 if (startoffset <= end) 2245 if (error)
2194 truncate_pagecache_range(VFS_I(ip), startoffset, end); 2246 return error;
2195 /* FALLTHRU */ 2247 setprealloc = 1;
2248 break;
2249
2196 case XFS_IOC_RESVSP: 2250 case XFS_IOC_RESVSP:
2197 case XFS_IOC_RESVSP64: 2251 case XFS_IOC_RESVSP64:
2198 error = xfs_alloc_file_space(ip, startoffset, bf->l_len, 2252 error = xfs_alloc_file_space(ip, startoffset, bf->l_len,
2199 prealloc_type, attr_flags); 2253 XFS_BMAPI_PREALLOC, attr_flags);
2200 if (error) 2254 if (error)
2201 return error; 2255 return error;
2202 setprealloc = 1; 2256 setprealloc = 1;