diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/ext4/inode.c | 63 |
1 files changed, 47 insertions, 16 deletions
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index ce47847bb37d..0fbe678d40bb 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c | |||
@@ -2298,6 +2298,29 @@ out: | |||
2298 | return ret; | 2298 | return ret; |
2299 | } | 2299 | } |
2300 | 2300 | ||
2301 | /* | ||
2302 | * Check if we should update i_disksize | ||
2303 | * when write to the end of file but not require block allocation | ||
2304 | */ | ||
2305 | static int ext4_da_should_update_i_disksize(struct page *page, | ||
2306 | unsigned long offset) | ||
2307 | { | ||
2308 | struct buffer_head *bh; | ||
2309 | struct inode *inode = page->mapping->host; | ||
2310 | unsigned int idx; | ||
2311 | int i; | ||
2312 | |||
2313 | bh = page_buffers(page); | ||
2314 | idx = offset >> inode->i_blkbits; | ||
2315 | |||
2316 | for (i=0; i < idx; i++) | ||
2317 | bh = bh->b_this_page; | ||
2318 | |||
2319 | if (!buffer_mapped(bh) || (buffer_delay(bh))) | ||
2320 | return 0; | ||
2321 | return 1; | ||
2322 | } | ||
2323 | |||
2301 | static int ext4_da_write_end(struct file *file, | 2324 | static int ext4_da_write_end(struct file *file, |
2302 | struct address_space *mapping, | 2325 | struct address_space *mapping, |
2303 | loff_t pos, unsigned len, unsigned copied, | 2326 | loff_t pos, unsigned len, unsigned copied, |
@@ -2307,6 +2330,10 @@ static int ext4_da_write_end(struct file *file, | |||
2307 | int ret = 0, ret2; | 2330 | int ret = 0, ret2; |
2308 | handle_t *handle = ext4_journal_current_handle(); | 2331 | handle_t *handle = ext4_journal_current_handle(); |
2309 | loff_t new_i_size; | 2332 | loff_t new_i_size; |
2333 | unsigned long start, end; | ||
2334 | |||
2335 | start = pos & (PAGE_CACHE_SIZE - 1); | ||
2336 | end = start + copied -1; | ||
2310 | 2337 | ||
2311 | /* | 2338 | /* |
2312 | * generic_write_end() will run mark_inode_dirty() if i_size | 2339 | * generic_write_end() will run mark_inode_dirty() if i_size |
@@ -2315,18 +2342,23 @@ static int ext4_da_write_end(struct file *file, | |||
2315 | */ | 2342 | */ |
2316 | 2343 | ||
2317 | new_i_size = pos + copied; | 2344 | new_i_size = pos + copied; |
2318 | if (new_i_size > EXT4_I(inode)->i_disksize) | 2345 | if (new_i_size > EXT4_I(inode)->i_disksize) { |
2319 | if (!walk_page_buffers(NULL, page_buffers(page), | 2346 | if (ext4_da_should_update_i_disksize(page, end)) { |
2320 | 0, len, NULL, ext4_bh_unmapped_or_delay)){ | 2347 | down_write(&EXT4_I(inode)->i_data_sem); |
2321 | /* | 2348 | if (new_i_size > EXT4_I(inode)->i_disksize) { |
2322 | * Updating i_disksize when extending file without | 2349 | /* |
2323 | * needing block allocation | 2350 | * Updating i_disksize when extending file |
2324 | */ | 2351 | * without needing block allocation |
2325 | if (ext4_should_order_data(inode)) | 2352 | */ |
2326 | ret = ext4_jbd2_file_inode(handle, inode); | 2353 | if (ext4_should_order_data(inode)) |
2354 | ret = ext4_jbd2_file_inode(handle, | ||
2355 | inode); | ||
2327 | 2356 | ||
2328 | EXT4_I(inode)->i_disksize = new_i_size; | 2357 | EXT4_I(inode)->i_disksize = new_i_size; |
2358 | } | ||
2359 | up_write(&EXT4_I(inode)->i_data_sem); | ||
2329 | } | 2360 | } |
2361 | } | ||
2330 | ret2 = generic_write_end(file, mapping, pos, len, copied, | 2362 | ret2 = generic_write_end(file, mapping, pos, len, copied, |
2331 | page, fsdata); | 2363 | page, fsdata); |
2332 | copied = ret2; | 2364 | copied = ret2; |
@@ -3394,6 +3426,11 @@ void ext4_truncate(struct inode *inode) | |||
3394 | goto out_stop; | 3426 | goto out_stop; |
3395 | 3427 | ||
3396 | /* | 3428 | /* |
3429 | * From here we block out all ext4_get_block() callers who want to | ||
3430 | * modify the block allocation tree. | ||
3431 | */ | ||
3432 | down_write(&ei->i_data_sem); | ||
3433 | /* | ||
3397 | * The orphan list entry will now protect us from any crash which | 3434 | * The orphan list entry will now protect us from any crash which |
3398 | * occurs before the truncate completes, so it is now safe to propagate | 3435 | * occurs before the truncate completes, so it is now safe to propagate |
3399 | * the new, shorter inode size (held for now in i_size) into the | 3436 | * the new, shorter inode size (held for now in i_size) into the |
@@ -3402,12 +3439,6 @@ void ext4_truncate(struct inode *inode) | |||
3402 | */ | 3439 | */ |
3403 | ei->i_disksize = inode->i_size; | 3440 | ei->i_disksize = inode->i_size; |
3404 | 3441 | ||
3405 | /* | ||
3406 | * From here we block out all ext4_get_block() callers who want to | ||
3407 | * modify the block allocation tree. | ||
3408 | */ | ||
3409 | down_write(&ei->i_data_sem); | ||
3410 | |||
3411 | if (n == 1) { /* direct blocks */ | 3442 | if (n == 1) { /* direct blocks */ |
3412 | ext4_free_data(handle, inode, NULL, i_data+offsets[0], | 3443 | ext4_free_data(handle, inode, NULL, i_data+offsets[0], |
3413 | i_data + EXT4_NDIR_BLOCKS); | 3444 | i_data + EXT4_NDIR_BLOCKS); |