Commit ecdc6394 authored by Evgeniy Dushistov's avatar Evgeniy Dushistov Committed by Linus Torvalds

[PATCH] ufs: truncate correction

1) When we allocated last fragment in ufs_truncate, we read page, check
   if block mapped to address, and if not trying to allocate it.  This is
   wrong behaviour, fragment may be NOT allocated, but mapped, this
   happened because of "block map" function not checked allocated fragment
   or not, it just take address of the first fragment in the block, add
   offset of fragment and return result, this is correct behaviour in
   almost all situation except call from ufs_truncate.

2) Almost all implementation of UFS, which I can investigate have such
   "defect": if you have full disk, and try truncate file, for example 3GB
   to 2MB, and have hole in this region, truncate return -ENOSPC.  I tried
   evade from this problem, but "block allocation" algorithm is tied to
   right value of i_lastfrag, and fix of this corner case may slow down of
   ordinaries scenarios, so this patch makes behavior of "truncate"
   operations similar to what other UFS implementations do.
Signed-off-by: default avatarEvgeniy Dushistov <dushistov@mail.ru>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent c37336b0
...@@ -375,17 +375,15 @@ static int ufs_alloc_lastblock(struct inode *inode) ...@@ -375,17 +375,15 @@ static int ufs_alloc_lastblock(struct inode *inode)
int err = 0; int err = 0;
struct address_space *mapping = inode->i_mapping; struct address_space *mapping = inode->i_mapping;
struct ufs_sb_private_info *uspi = UFS_SB(inode->i_sb)->s_uspi; struct ufs_sb_private_info *uspi = UFS_SB(inode->i_sb)->s_uspi;
struct ufs_inode_info *ufsi = UFS_I(inode);
unsigned lastfrag, i, end; unsigned lastfrag, i, end;
struct page *lastpage; struct page *lastpage;
struct buffer_head *bh; struct buffer_head *bh;
lastfrag = (i_size_read(inode) + uspi->s_fsize - 1) >> uspi->s_fshift; lastfrag = (i_size_read(inode) + uspi->s_fsize - 1) >> uspi->s_fshift;
if (!lastfrag) { if (!lastfrag)
ufsi->i_lastfrag = 0;
goto out; goto out;
}
lastfrag--; lastfrag--;
lastpage = ufs_get_locked_page(mapping, lastfrag >> lastpage = ufs_get_locked_page(mapping, lastfrag >>
...@@ -400,25 +398,25 @@ static int ufs_alloc_lastblock(struct inode *inode) ...@@ -400,25 +398,25 @@ static int ufs_alloc_lastblock(struct inode *inode)
for (i = 0; i < end; ++i) for (i = 0; i < end; ++i)
bh = bh->b_this_page; bh = bh->b_this_page;
if (!buffer_mapped(bh)) {
err = ufs_getfrag_block(inode, lastfrag, bh, 1); err = ufs_getfrag_block(inode, lastfrag, bh, 1);
if (unlikely(err)) if (unlikely(err))
goto out_unlock; goto out_unlock;
if (buffer_new(bh)) { if (buffer_new(bh)) {
clear_buffer_new(bh); clear_buffer_new(bh);
unmap_underlying_metadata(bh->b_bdev, unmap_underlying_metadata(bh->b_bdev,
bh->b_blocknr); bh->b_blocknr);
/* /*
* we do not zeroize fragment, because of * we do not zeroize fragment, because of
* if it maped to hole, it already contains zeroes * if it maped to hole, it already contains zeroes
*/ */
set_buffer_uptodate(bh); set_buffer_uptodate(bh);
mark_buffer_dirty(bh); mark_buffer_dirty(bh);
set_page_dirty(lastpage); set_page_dirty(lastpage);
}
} }
out_unlock: out_unlock:
ufs_put_locked_page(lastpage); ufs_put_locked_page(lastpage);
out: out:
...@@ -440,23 +438,11 @@ int ufs_truncate(struct inode *inode, loff_t old_i_size) ...@@ -440,23 +438,11 @@ int ufs_truncate(struct inode *inode, loff_t old_i_size)
if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
return -EPERM; return -EPERM;
if (inode->i_size > old_i_size) { err = ufs_alloc_lastblock(inode);
/*
* if we expand file we should care about
* allocation of block for last byte first of all
*/
err = ufs_alloc_lastblock(inode);
if (err) { if (err) {
i_size_write(inode, old_i_size); i_size_write(inode, old_i_size);
goto out; goto out;
}
/*
* go away, because of we expand file, and we do not
* need free blocks, and zeroizes page
*/
lock_kernel();
goto almost_end;
} }
block_truncate_page(inode->i_mapping, inode->i_size, ufs_getfrag_block); block_truncate_page(inode->i_mapping, inode->i_size, ufs_getfrag_block);
...@@ -477,21 +463,8 @@ int ufs_truncate(struct inode *inode, loff_t old_i_size) ...@@ -477,21 +463,8 @@ int ufs_truncate(struct inode *inode, loff_t old_i_size)
yield(); yield();
} }
if (inode->i_size < old_i_size) {
/*
* now we should have enough space
* to allocate block for last byte
*/
err = ufs_alloc_lastblock(inode);
if (err)
/*
* looks like all the same - we have no space,
* but we truncate file already
*/
inode->i_size = (ufsi->i_lastfrag - 1) * uspi->s_fsize;
}
almost_end:
inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC; inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC;
ufsi->i_lastfrag = DIRECT_FRAGMENT;
unlock_kernel(); unlock_kernel();
mark_inode_dirty(inode); mark_inode_dirty(inode);
out: out:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment