aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorQu Wenruo <quwenruo@cn.fujitsu.com>2014-05-30 03:16:10 -0400
committerChris Mason <clm@fb.com>2014-06-09 20:21:06 -0400
commitd77815461f047e561f77a07754ae923ade597d4e (patch)
tree0d0a59e05a2e96aa9d6cafb7c7170877b64fb588 /fs
parent3821f348889e506efbd268cc8149e0ebfa47c4e5 (diff)
btrfs: Avoid trucating page or punching hole in a already existed hole.
btrfs_punch_hole() will truncate unaligned pages or punch hole on a already existed hole. This will cause unneeded zero page or holes splitting the original huge hole. This patch will skip already existed holes before any page truncating or hole punching. Signed-off-by: Qu Wenruo <quwenruo@cn.fujitsu.com> Signed-off-by: Chris Mason <clm@fb.com>
Diffstat (limited to 'fs')
-rw-r--r--fs/btrfs/file.c112
1 files changed, 98 insertions, 14 deletions
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index e46bfaf6cde2..eb3f2708a01d 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -2184,6 +2184,37 @@ out:
2184 return 0; 2184 return 0;
2185} 2185}
2186 2186
2187/*
2188 * Find a hole extent on given inode and change start/len to the end of hole
2189 * extent.(hole/vacuum extent whose em->start <= start &&
2190 * em->start + em->len > start)
2191 * When a hole extent is found, return 1 and modify start/len.
2192 */
2193static int find_first_non_hole(struct inode *inode, u64 *start, u64 *len)
2194{
2195 struct extent_map *em;
2196 int ret = 0;
2197
2198 em = btrfs_get_extent(inode, NULL, 0, *start, *len, 0);
2199 if (IS_ERR_OR_NULL(em)) {
2200 if (!em)
2201 ret = -ENOMEM;
2202 else
2203 ret = PTR_ERR(em);
2204 return ret;
2205 }
2206
2207 /* Hole or vacuum extent(only exists in no-hole mode) */
2208 if (em->block_start == EXTENT_MAP_HOLE) {
2209 ret = 1;
2210 *len = em->start + em->len > *start + *len ?
2211 0 : *start + *len - em->start - em->len;
2212 *start = em->start + em->len;
2213 }
2214 free_extent_map(em);
2215 return ret;
2216}
2217
2187static int btrfs_punch_hole(struct inode *inode, loff_t offset, loff_t len) 2218static int btrfs_punch_hole(struct inode *inode, loff_t offset, loff_t len)
2188{ 2219{
2189 struct btrfs_root *root = BTRFS_I(inode)->root; 2220 struct btrfs_root *root = BTRFS_I(inode)->root;
@@ -2191,17 +2222,18 @@ static int btrfs_punch_hole(struct inode *inode, loff_t offset, loff_t len)
2191 struct btrfs_path *path; 2222 struct btrfs_path *path;
2192 struct btrfs_block_rsv *rsv; 2223 struct btrfs_block_rsv *rsv;
2193 struct btrfs_trans_handle *trans; 2224 struct btrfs_trans_handle *trans;
2194 u64 lockstart = round_up(offset, BTRFS_I(inode)->root->sectorsize); 2225 u64 lockstart;
2195 u64 lockend = round_down(offset + len, 2226 u64 lockend;
2196 BTRFS_I(inode)->root->sectorsize) - 1; 2227 u64 tail_start;
2197 u64 cur_offset = lockstart; 2228 u64 tail_len;
2229 u64 orig_start = offset;
2230 u64 cur_offset;
2198 u64 min_size = btrfs_calc_trunc_metadata_size(root, 1); 2231 u64 min_size = btrfs_calc_trunc_metadata_size(root, 1);
2199 u64 drop_end; 2232 u64 drop_end;
2200 int ret = 0; 2233 int ret = 0;
2201 int err = 0; 2234 int err = 0;
2202 int rsv_count; 2235 int rsv_count;
2203 bool same_page = ((offset >> PAGE_CACHE_SHIFT) == 2236 bool same_page;
2204 ((offset + len - 1) >> PAGE_CACHE_SHIFT));
2205 bool no_holes = btrfs_fs_incompat(root->fs_info, NO_HOLES); 2237 bool no_holes = btrfs_fs_incompat(root->fs_info, NO_HOLES);
2206 u64 ino_size; 2238 u64 ino_size;
2207 2239
@@ -2211,6 +2243,21 @@ static int btrfs_punch_hole(struct inode *inode, loff_t offset, loff_t len)
2211 2243
2212 mutex_lock(&inode->i_mutex); 2244 mutex_lock(&inode->i_mutex);
2213 ino_size = round_up(inode->i_size, PAGE_CACHE_SIZE); 2245 ino_size = round_up(inode->i_size, PAGE_CACHE_SIZE);
2246 ret = find_first_non_hole(inode, &offset, &len);
2247 if (ret < 0)
2248 goto out_only_mutex;
2249 if (ret && !len) {
2250 /* Already in a large hole */
2251 ret = 0;
2252 goto out_only_mutex;
2253 }
2254
2255 lockstart = round_up(offset , BTRFS_I(inode)->root->sectorsize);
2256 lockend = round_down(offset + len,
2257 BTRFS_I(inode)->root->sectorsize) - 1;
2258 same_page = ((offset >> PAGE_CACHE_SHIFT) ==
2259 ((offset + len - 1) >> PAGE_CACHE_SHIFT));
2260
2214 /* 2261 /*
2215 * We needn't truncate any page which is beyond the end of the file 2262 * We needn't truncate any page which is beyond the end of the file
2216 * because we are sure there is no data there. 2263 * because we are sure there is no data there.
@@ -2222,8 +2269,7 @@ static int btrfs_punch_hole(struct inode *inode, loff_t offset, loff_t len)
2222 if (same_page && len < PAGE_CACHE_SIZE) { 2269 if (same_page && len < PAGE_CACHE_SIZE) {
2223 if (offset < ino_size) 2270 if (offset < ino_size)
2224 ret = btrfs_truncate_page(inode, offset, len, 0); 2271 ret = btrfs_truncate_page(inode, offset, len, 0);
2225 mutex_unlock(&inode->i_mutex); 2272 goto out_only_mutex;
2226 return ret;
2227 } 2273 }
2228 2274
2229 /* zero back part of the first page */ 2275 /* zero back part of the first page */
@@ -2235,12 +2281,39 @@ static int btrfs_punch_hole(struct inode *inode, loff_t offset, loff_t len)
2235 } 2281 }
2236 } 2282 }
2237 2283
2238 /* zero the front end of the last page */ 2284 /* Check the aligned pages after the first unaligned page,
2239 if (offset + len < ino_size) { 2285 * if offset != orig_start, which means the first unaligned page
2240 ret = btrfs_truncate_page(inode, offset + len, 0, 1); 2286 * including serveral following pages are already in holes,
2241 if (ret) { 2287 * the extra check can be skipped */
2242 mutex_unlock(&inode->i_mutex); 2288 if (offset == orig_start) {
2243 return ret; 2289 /* after truncate page, check hole again */
2290 len = offset + len - lockstart;
2291 offset = lockstart;
2292 ret = find_first_non_hole(inode, &offset, &len);
2293 if (ret < 0)
2294 goto out_only_mutex;
2295 if (ret && !len) {
2296 ret = 0;
2297 goto out_only_mutex;
2298 }
2299 lockstart = offset;
2300 }
2301
2302 /* Check the tail unaligned part is in a hole */
2303 tail_start = lockend + 1;
2304 tail_len = offset + len - tail_start;
2305 if (tail_len) {
2306 ret = find_first_non_hole(inode, &tail_start, &tail_len);
2307 if (unlikely(ret < 0))
2308 goto out_only_mutex;
2309 if (!ret) {
2310 /* zero the front end of the last page */
2311 if (tail_start + tail_len < ino_size) {
2312 ret = btrfs_truncate_page(inode,
2313 tail_start + tail_len, 0, 1);
2314 if (ret)
2315 goto out_only_mutex;
2316 }
2244 } 2317 }
2245 } 2318 }
2246 2319
@@ -2314,6 +2387,8 @@ static int btrfs_punch_hole(struct inode *inode, loff_t offset, loff_t len)
2314 BUG_ON(ret); 2387 BUG_ON(ret);
2315 trans->block_rsv = rsv; 2388 trans->block_rsv = rsv;
2316 2389
2390 cur_offset = lockstart;
2391 len = lockend - cur_offset;
2317 while (cur_offset < lockend) { 2392 while (cur_offset < lockend) {
2318 ret = __btrfs_drop_extents(trans, root, inode, path, 2393 ret = __btrfs_drop_extents(trans, root, inode, path,
2319 cur_offset, lockend + 1, 2394 cur_offset, lockend + 1,
@@ -2354,6 +2429,14 @@ static int btrfs_punch_hole(struct inode *inode, loff_t offset, loff_t len)
2354 rsv, min_size); 2429 rsv, min_size);
2355 BUG_ON(ret); /* shouldn't happen */ 2430 BUG_ON(ret); /* shouldn't happen */
2356 trans->block_rsv = rsv; 2431 trans->block_rsv = rsv;
2432
2433 ret = find_first_non_hole(inode, &cur_offset, &len);
2434 if (unlikely(ret < 0))
2435 break;
2436 if (ret && !len) {
2437 ret = 0;
2438 break;
2439 }
2357 } 2440 }
2358 2441
2359 if (ret) { 2442 if (ret) {
@@ -2392,6 +2475,7 @@ out_free:
2392out: 2475out:
2393 unlock_extent_cached(&BTRFS_I(inode)->io_tree, lockstart, lockend, 2476 unlock_extent_cached(&BTRFS_I(inode)->io_tree, lockstart, lockend,
2394 &cached_state, GFP_NOFS); 2477 &cached_state, GFP_NOFS);
2478out_only_mutex:
2395 mutex_unlock(&inode->i_mutex); 2479 mutex_unlock(&inode->i_mutex);
2396 if (ret && !err) 2480 if (ret && !err)
2397 err = ret; 2481 err = ret;