aboutsummaryrefslogtreecommitdiffstats
path: root/fs/ext4
diff options
context:
space:
mode:
authorAneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>2008-09-13 13:06:18 -0400
committerTheodore Ts'o <tytso@mit.edu>2008-09-13 13:06:18 -0400
commitcf17fea6575cb1739552e1d0cb2b446305ee3d0c (patch)
tree319e1044e271aeb31ad14847f369776ac5db74db /fs/ext4
parentae4d537211ff250a8c23c4f1227c4276cd2508ab (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.h11
-rw-r--r--fs/ext4/extents.c9
-rw-r--r--fs/ext4/inode.c54
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
1221static 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,