aboutsummaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--fs/xfs/xfs_file.c2
-rw-r--r--fs/xfs/xfs_vnodeops.c96
-rw-r--r--fs/xfs/xfs_vnodeops.h1
3 files changed, 77 insertions, 22 deletions
diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c
index 400b187595bb..67284edb84d7 100644
--- a/fs/xfs/xfs_file.c
+++ b/fs/xfs/xfs_file.c
@@ -86,7 +86,7 @@ xfs_rw_ilock_demote(
86 * valid before the operation, it will be read from disk before 86 * valid before the operation, it will be read from disk before
87 * being partially zeroed. 87 * being partially zeroed.
88 */ 88 */
89STATIC int 89int
90xfs_iozero( 90xfs_iozero(
91 struct xfs_inode *ip, /* inode */ 91 struct xfs_inode *ip, /* inode */
92 loff_t pos, /* offset in file */ 92 loff_t pos, /* offset in file */
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;
diff --git a/fs/xfs/xfs_vnodeops.h b/fs/xfs/xfs_vnodeops.h
index 91a03fa3814f..5163022d9808 100644
--- a/fs/xfs/xfs_vnodeops.h
+++ b/fs/xfs/xfs_vnodeops.h
@@ -49,6 +49,7 @@ int xfs_attr_remove(struct xfs_inode *dp, const unsigned char *name, int flags);
49int xfs_attr_list(struct xfs_inode *dp, char *buffer, int bufsize, 49int xfs_attr_list(struct xfs_inode *dp, char *buffer, int bufsize,
50 int flags, struct attrlist_cursor_kern *cursor); 50 int flags, struct attrlist_cursor_kern *cursor);
51 51
52int xfs_iozero(struct xfs_inode *, loff_t, size_t);
52int xfs_zero_eof(struct xfs_inode *, xfs_off_t, xfs_fsize_t); 53int xfs_zero_eof(struct xfs_inode *, xfs_off_t, xfs_fsize_t);
53int xfs_free_eofblocks(struct xfs_mount *, struct xfs_inode *, bool); 54int xfs_free_eofblocks(struct xfs_mount *, struct xfs_inode *, bool);
54 55