diff options
author | Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com> | 2008-09-13 13:06:18 -0400 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 2008-09-13 13:06:18 -0400 |
commit | cf17fea6575cb1739552e1d0cb2b446305ee3d0c (patch) | |
tree | 319e1044e271aeb31ad14847f369776ac5db74db /fs/ext4 | |
parent | ae4d537211ff250a8c23c4f1227c4276cd2508ab (diff) |
ext4: Properly update i_disksize.
With delayed allocation we use i_data_sem to update i_disksize. We need
to update i_disksize only if the new size specified is greater than the
current value and we need to make sure we don't race with other
i_disksize update. With delayed allocation we will switch to the
write_begin function for non-delayed allocation if we are low on free
blocks. This means the write_begin function for non-delayed allocation
also needs to use the same locking.
We also need to check and update i_disksize even if the new size is less
that inode.i_size because of delayed allocation.
Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
Diffstat (limited to 'fs/ext4')
-rw-r--r-- | fs/ext4/ext4.h | 11 | ||||
-rw-r--r-- | fs/ext4/extents.c | 9 | ||||
-rw-r--r-- | fs/ext4/inode.c | 54 |
3 files changed, 46 insertions, 28 deletions
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index e13b9deee866..3e47b99a763c 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h | |||
@@ -1218,6 +1218,17 @@ do { \ | |||
1218 | #define EXT4_FREEBLOCKS_WATERMARK 0 | 1218 | #define EXT4_FREEBLOCKS_WATERMARK 0 |
1219 | #endif | 1219 | #endif |
1220 | 1220 | ||
1221 | static inline void ext4_update_i_disksize(struct inode *inode, loff_t newsize) | ||
1222 | { | ||
1223 | /* | ||
1224 | * XXX: replace with spinlock if seen contended -bzzz | ||
1225 | */ | ||
1226 | down_write(&EXT4_I(inode)->i_data_sem); | ||
1227 | if (newsize > EXT4_I(inode)->i_disksize) | ||
1228 | EXT4_I(inode)->i_disksize = newsize; | ||
1229 | up_write(&EXT4_I(inode)->i_data_sem); | ||
1230 | return ; | ||
1231 | } | ||
1221 | 1232 | ||
1222 | /* | 1233 | /* |
1223 | * Inodes and files operations | 1234 | * Inodes and files operations |
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index 797f0602a68f..e8758df2617b 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c | |||
@@ -2878,10 +2878,11 @@ static void ext4_falloc_update_inode(struct inode *inode, | |||
2878 | * Update only when preallocation was requested beyond | 2878 | * Update only when preallocation was requested beyond |
2879 | * the file size. | 2879 | * the file size. |
2880 | */ | 2880 | */ |
2881 | if (!(mode & FALLOC_FL_KEEP_SIZE) && | 2881 | if (!(mode & FALLOC_FL_KEEP_SIZE)) { |
2882 | new_size > i_size_read(inode)) { | 2882 | if (new_size > i_size_read(inode)) |
2883 | i_size_write(inode, new_size); | 2883 | i_size_write(inode, new_size); |
2884 | EXT4_I(inode)->i_disksize = new_size; | 2884 | if (new_size > EXT4_I(inode)->i_disksize) |
2885 | ext4_update_i_disksize(inode, new_size); | ||
2885 | } | 2886 | } |
2886 | 2887 | ||
2887 | } | 2888 | } |
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 634f0bc75700..22fcbb67cd88 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c | |||
@@ -1434,16 +1434,18 @@ static int ext4_ordered_write_end(struct file *file, | |||
1434 | ret = ext4_jbd2_file_inode(handle, inode); | 1434 | ret = ext4_jbd2_file_inode(handle, inode); |
1435 | 1435 | ||
1436 | if (ret == 0) { | 1436 | if (ret == 0) { |
1437 | /* | ||
1438 | * generic_write_end() will run mark_inode_dirty() if i_size | ||
1439 | * changes. So let's piggyback the i_disksize mark_inode_dirty | ||
1440 | * into that. | ||
1441 | */ | ||
1442 | loff_t new_i_size; | 1437 | loff_t new_i_size; |
1443 | 1438 | ||
1444 | new_i_size = pos + copied; | 1439 | new_i_size = pos + copied; |
1445 | if (new_i_size > EXT4_I(inode)->i_disksize) | 1440 | if (new_i_size > EXT4_I(inode)->i_disksize) { |
1446 | EXT4_I(inode)->i_disksize = new_i_size; | 1441 | ext4_update_i_disksize(inode, new_i_size); |
1442 | /* We need to mark inode dirty even if | ||
1443 | * new_i_size is less that inode->i_size | ||
1444 | * bu greater than i_disksize.(hint delalloc) | ||
1445 | */ | ||
1446 | ext4_mark_inode_dirty(handle, inode); | ||
1447 | } | ||
1448 | |||
1447 | ret2 = generic_write_end(file, mapping, pos, len, copied, | 1449 | ret2 = generic_write_end(file, mapping, pos, len, copied, |
1448 | page, fsdata); | 1450 | page, fsdata); |
1449 | copied = ret2; | 1451 | copied = ret2; |
@@ -1468,8 +1470,14 @@ static int ext4_writeback_write_end(struct file *file, | |||
1468 | loff_t new_i_size; | 1470 | loff_t new_i_size; |
1469 | 1471 | ||
1470 | new_i_size = pos + copied; | 1472 | new_i_size = pos + copied; |
1471 | if (new_i_size > EXT4_I(inode)->i_disksize) | 1473 | if (new_i_size > EXT4_I(inode)->i_disksize) { |
1472 | EXT4_I(inode)->i_disksize = new_i_size; | 1474 | ext4_update_i_disksize(inode, new_i_size); |
1475 | /* We need to mark inode dirty even if | ||
1476 | * new_i_size is less that inode->i_size | ||
1477 | * bu greater than i_disksize.(hint delalloc) | ||
1478 | */ | ||
1479 | ext4_mark_inode_dirty(handle, inode); | ||
1480 | } | ||
1473 | 1481 | ||
1474 | ret2 = generic_write_end(file, mapping, pos, len, copied, | 1482 | ret2 = generic_write_end(file, mapping, pos, len, copied, |
1475 | page, fsdata); | 1483 | page, fsdata); |
@@ -1494,6 +1502,7 @@ static int ext4_journalled_write_end(struct file *file, | |||
1494 | int ret = 0, ret2; | 1502 | int ret = 0, ret2; |
1495 | int partial = 0; | 1503 | int partial = 0; |
1496 | unsigned from, to; | 1504 | unsigned from, to; |
1505 | loff_t new_i_size; | ||
1497 | 1506 | ||
1498 | from = pos & (PAGE_CACHE_SIZE - 1); | 1507 | from = pos & (PAGE_CACHE_SIZE - 1); |
1499 | to = from + len; | 1508 | to = from + len; |
@@ -1508,11 +1517,12 @@ static int ext4_journalled_write_end(struct file *file, | |||
1508 | to, &partial, write_end_fn); | 1517 | to, &partial, write_end_fn); |
1509 | if (!partial) | 1518 | if (!partial) |
1510 | SetPageUptodate(page); | 1519 | SetPageUptodate(page); |
1511 | if (pos+copied > inode->i_size) | 1520 | new_i_size = pos + copied; |
1521 | if (new_i_size > inode->i_size) | ||
1512 | i_size_write(inode, pos+copied); | 1522 | i_size_write(inode, pos+copied); |
1513 | EXT4_I(inode)->i_state |= EXT4_STATE_JDATA; | 1523 | EXT4_I(inode)->i_state |= EXT4_STATE_JDATA; |
1514 | if (inode->i_size > EXT4_I(inode)->i_disksize) { | 1524 | if (new_i_size > EXT4_I(inode)->i_disksize) { |
1515 | EXT4_I(inode)->i_disksize = inode->i_size; | 1525 | ext4_update_i_disksize(inode, new_i_size); |
1516 | ret2 = ext4_mark_inode_dirty(handle, inode); | 1526 | ret2 = ext4_mark_inode_dirty(handle, inode); |
1517 | if (!ret) | 1527 | if (!ret) |
1518 | ret = ret2; | 1528 | ret = ret2; |
@@ -2227,18 +2237,9 @@ static int ext4_da_get_block_write(struct inode *inode, sector_t iblock, | |||
2227 | if (disksize > i_size_read(inode)) | 2237 | if (disksize > i_size_read(inode)) |
2228 | disksize = i_size_read(inode); | 2238 | disksize = i_size_read(inode); |
2229 | if (disksize > EXT4_I(inode)->i_disksize) { | 2239 | if (disksize > EXT4_I(inode)->i_disksize) { |
2230 | /* | 2240 | ext4_update_i_disksize(inode, disksize); |
2231 | * XXX: replace with spinlock if seen contended -bzzz | 2241 | ret = ext4_mark_inode_dirty(handle, inode); |
2232 | */ | 2242 | return ret; |
2233 | down_write(&EXT4_I(inode)->i_data_sem); | ||
2234 | if (disksize > EXT4_I(inode)->i_disksize) | ||
2235 | EXT4_I(inode)->i_disksize = disksize; | ||
2236 | up_write(&EXT4_I(inode)->i_data_sem); | ||
2237 | |||
2238 | if (EXT4_I(inode)->i_disksize == disksize) { | ||
2239 | ret = ext4_mark_inode_dirty(handle, inode); | ||
2240 | return ret; | ||
2241 | } | ||
2242 | } | 2243 | } |
2243 | ret = 0; | 2244 | ret = 0; |
2244 | } | 2245 | } |
@@ -2654,6 +2655,11 @@ static int ext4_da_write_end(struct file *file, | |||
2654 | EXT4_I(inode)->i_disksize = new_i_size; | 2655 | EXT4_I(inode)->i_disksize = new_i_size; |
2655 | } | 2656 | } |
2656 | up_write(&EXT4_I(inode)->i_data_sem); | 2657 | up_write(&EXT4_I(inode)->i_data_sem); |
2658 | /* We need to mark inode dirty even if | ||
2659 | * new_i_size is less that inode->i_size | ||
2660 | * bu greater than i_disksize.(hint delalloc) | ||
2661 | */ | ||
2662 | ext4_mark_inode_dirty(handle, inode); | ||
2657 | } | 2663 | } |
2658 | } | 2664 | } |
2659 | ret2 = generic_write_end(file, mapping, pos, len, copied, | 2665 | ret2 = generic_write_end(file, mapping, pos, len, copied, |