diff options
Diffstat (limited to 'fs/ext4')
-rw-r--r-- | fs/ext4/extents.c | 63 |
1 files changed, 63 insertions, 0 deletions
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index d279ea8c4661..668d82e494dd 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c | |||
@@ -2212,6 +2212,8 @@ static int ext4_ext_zeroout(struct inode *inode, struct ext4_extent *ex) | |||
2212 | return ret; | 2212 | return ret; |
2213 | } | 2213 | } |
2214 | 2214 | ||
2215 | #define EXT4_EXT_ZERO_LEN 7 | ||
2216 | |||
2215 | /* | 2217 | /* |
2216 | * This function is called by ext4_ext_get_blocks() if someone tries to write | 2218 | * This function is called by ext4_ext_get_blocks() if someone tries to write |
2217 | * to an uninitialized extent. It may result in splitting the uninitialized | 2219 | * to an uninitialized extent. It may result in splitting the uninitialized |
@@ -2254,6 +2256,18 @@ static int ext4_ext_convert_to_initialized(handle_t *handle, | |||
2254 | err = ext4_ext_get_access(handle, inode, path + depth); | 2256 | err = ext4_ext_get_access(handle, inode, path + depth); |
2255 | if (err) | 2257 | if (err) |
2256 | goto out; | 2258 | goto out; |
2259 | /* If extent has less than 2*EXT4_EXT_ZERO_LEN zerout directly */ | ||
2260 | if (ee_len <= 2*EXT4_EXT_ZERO_LEN) { | ||
2261 | err = ext4_ext_zeroout(inode, &orig_ex); | ||
2262 | if (err) | ||
2263 | goto fix_extent_len; | ||
2264 | /* update the extent length and mark as initialized */ | ||
2265 | ex->ee_block = orig_ex.ee_block; | ||
2266 | ex->ee_len = orig_ex.ee_len; | ||
2267 | ext4_ext_store_pblock(ex, ext_pblock(&orig_ex)); | ||
2268 | ext4_ext_dirty(handle, inode, path + depth); | ||
2269 | return le16_to_cpu(ex->ee_len); | ||
2270 | } | ||
2257 | 2271 | ||
2258 | /* ex1: ee_block to iblock - 1 : uninitialized */ | 2272 | /* ex1: ee_block to iblock - 1 : uninitialized */ |
2259 | if (iblock > ee_block) { | 2273 | if (iblock > ee_block) { |
@@ -2272,6 +2286,38 @@ static int ext4_ext_convert_to_initialized(handle_t *handle, | |||
2272 | /* ex3: to ee_block + ee_len : uninitialised */ | 2286 | /* ex3: to ee_block + ee_len : uninitialised */ |
2273 | if (allocated > max_blocks) { | 2287 | if (allocated > max_blocks) { |
2274 | unsigned int newdepth; | 2288 | unsigned int newdepth; |
2289 | /* If extent has less than EXT4_EXT_ZERO_LEN zerout directly */ | ||
2290 | if (allocated <= EXT4_EXT_ZERO_LEN) { | ||
2291 | /* Mark first half uninitialized. | ||
2292 | * Mark second half initialized and zero out the | ||
2293 | * initialized extent | ||
2294 | */ | ||
2295 | ex->ee_block = orig_ex.ee_block; | ||
2296 | ex->ee_len = cpu_to_le16(ee_len - allocated); | ||
2297 | ext4_ext_mark_uninitialized(ex); | ||
2298 | ext4_ext_store_pblock(ex, ext_pblock(&orig_ex)); | ||
2299 | ext4_ext_dirty(handle, inode, path + depth); | ||
2300 | |||
2301 | ex3 = &newex; | ||
2302 | ex3->ee_block = cpu_to_le32(iblock); | ||
2303 | ext4_ext_store_pblock(ex3, newblock); | ||
2304 | ex3->ee_len = cpu_to_le16(allocated); | ||
2305 | err = ext4_ext_insert_extent(handle, inode, path, ex3); | ||
2306 | if (err == -ENOSPC) { | ||
2307 | err = ext4_ext_zeroout(inode, &orig_ex); | ||
2308 | if (err) | ||
2309 | goto fix_extent_len; | ||
2310 | ex->ee_block = orig_ex.ee_block; | ||
2311 | ex->ee_len = orig_ex.ee_len; | ||
2312 | ext4_ext_store_pblock(ex, ext_pblock(&orig_ex)); | ||
2313 | ext4_ext_dirty(handle, inode, path + depth); | ||
2314 | return le16_to_cpu(ex->ee_len); | ||
2315 | |||
2316 | } else if (err) | ||
2317 | goto fix_extent_len; | ||
2318 | |||
2319 | return allocated; | ||
2320 | } | ||
2275 | ex3 = &newex; | 2321 | ex3 = &newex; |
2276 | ex3->ee_block = cpu_to_le32(iblock + max_blocks); | 2322 | ex3->ee_block = cpu_to_le32(iblock + max_blocks); |
2277 | ext4_ext_store_pblock(ex3, newblock + max_blocks); | 2323 | ext4_ext_store_pblock(ex3, newblock + max_blocks); |
@@ -2320,6 +2366,23 @@ static int ext4_ext_convert_to_initialized(handle_t *handle, | |||
2320 | goto out; | 2366 | goto out; |
2321 | } | 2367 | } |
2322 | allocated = max_blocks; | 2368 | allocated = max_blocks; |
2369 | |||
2370 | /* If extent has less than EXT4_EXT_ZERO_LEN and we are trying | ||
2371 | * to insert a extent in the middle zerout directly | ||
2372 | * otherwise give the extent a chance to merge to left | ||
2373 | */ | ||
2374 | if (le16_to_cpu(orig_ex.ee_len) <= EXT4_EXT_ZERO_LEN && | ||
2375 | iblock != ee_block) { | ||
2376 | err = ext4_ext_zeroout(inode, &orig_ex); | ||
2377 | if (err) | ||
2378 | goto fix_extent_len; | ||
2379 | /* update the extent length and mark as initialized */ | ||
2380 | ex->ee_block = orig_ex.ee_block; | ||
2381 | ex->ee_len = orig_ex.ee_len; | ||
2382 | ext4_ext_store_pblock(ex, ext_pblock(&orig_ex)); | ||
2383 | ext4_ext_dirty(handle, inode, path + depth); | ||
2384 | return le16_to_cpu(ex->ee_len); | ||
2385 | } | ||
2323 | } | 2386 | } |
2324 | /* | 2387 | /* |
2325 | * If there was a change of depth as part of the | 2388 | * If there was a change of depth as part of the |