aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDmitry Monakhov <dmonakhov@openvz.org>2010-02-16 11:33:42 -0500
committerJan Kara <jack@suse.cz>2010-03-04 18:20:26 -0500
commite5472147e1c0712d95d973acfdbd862957c77add (patch)
treea1e48e5e57931e53c3c9991b5005dd911299a71a
parentac0e773718dc20551e72900d2e7eada96ac91100 (diff)
ext3: quota_write cross block boundary behaviour
We always assume what dquot update result in changes in one data block But ext3_quota_write() function may handle cross block boundary writes In fact if this ever happen it will result in incorrect journal credits reservation. And later bug_on triggering. As soon this never happen the boundary cross loop is NOOP. In order to make things straight let's remove this loop and assert cross boundary condition. Signed-off-by: Dmitry Monakhov <dmonakhov@openvz.org> Signed-off-by: Jan Kara <jack@suse.cz>
-rw-r--r--fs/ext3/super.c69
1 files changed, 34 insertions, 35 deletions
diff --git a/fs/ext3/super.c b/fs/ext3/super.c
index 241c520b3081..5c54e5f685d4 100644
--- a/fs/ext3/super.c
+++ b/fs/ext3/super.c
@@ -2948,9 +2948,7 @@ static ssize_t ext3_quota_write(struct super_block *sb, int type,
2948 sector_t blk = off >> EXT3_BLOCK_SIZE_BITS(sb); 2948 sector_t blk = off >> EXT3_BLOCK_SIZE_BITS(sb);
2949 int err = 0; 2949 int err = 0;
2950 int offset = off & (sb->s_blocksize - 1); 2950 int offset = off & (sb->s_blocksize - 1);
2951 int tocopy;
2952 int journal_quota = EXT3_SB(sb)->s_qf_names[type] != NULL; 2951 int journal_quota = EXT3_SB(sb)->s_qf_names[type] != NULL;
2953 size_t towrite = len;
2954 struct buffer_head *bh; 2952 struct buffer_head *bh;
2955 handle_t *handle = journal_current_handle(); 2953 handle_t *handle = journal_current_handle();
2956 2954
@@ -2961,53 +2959,54 @@ static ssize_t ext3_quota_write(struct super_block *sb, int type,
2961 (unsigned long long)off, (unsigned long long)len); 2959 (unsigned long long)off, (unsigned long long)len);
2962 return -EIO; 2960 return -EIO;
2963 } 2961 }
2962
2963 /*
2964 * Since we account only one data block in transaction credits,
2965 * then it is impossible to cross a block boundary.
2966 */
2967 if (sb->s_blocksize - offset < len) {
2968 ext3_msg(sb, KERN_WARNING, "Quota write (off=%llu, len=%llu)"
2969 " cancelled because not block aligned",
2970 (unsigned long long)off, (unsigned long long)len);
2971 return -EIO;
2972 }
2964 mutex_lock_nested(&inode->i_mutex, I_MUTEX_QUOTA); 2973 mutex_lock_nested(&inode->i_mutex, I_MUTEX_QUOTA);
2965 while (towrite > 0) { 2974 bh = ext3_bread(handle, inode, blk, 1, &err);
2966 tocopy = sb->s_blocksize - offset < towrite ? 2975 if (!bh)
2967 sb->s_blocksize - offset : towrite; 2976 goto out;
2968 bh = ext3_bread(handle, inode, blk, 1, &err); 2977 if (journal_quota) {
2969 if (!bh) 2978 err = ext3_journal_get_write_access(handle, bh);
2979 if (err) {
2980 brelse(bh);
2970 goto out; 2981 goto out;
2971 if (journal_quota) {
2972 err = ext3_journal_get_write_access(handle, bh);
2973 if (err) {
2974 brelse(bh);
2975 goto out;
2976 }
2977 }
2978 lock_buffer(bh);
2979 memcpy(bh->b_data+offset, data, tocopy);
2980 flush_dcache_page(bh->b_page);
2981 unlock_buffer(bh);
2982 if (journal_quota)
2983 err = ext3_journal_dirty_metadata(handle, bh);
2984 else {
2985 /* Always do at least ordered writes for quotas */
2986 err = ext3_journal_dirty_data(handle, bh);
2987 mark_buffer_dirty(bh);
2988 } 2982 }
2989 brelse(bh);
2990 if (err)
2991 goto out;
2992 offset = 0;
2993 towrite -= tocopy;
2994 data += tocopy;
2995 blk++;
2996 } 2983 }
2984 lock_buffer(bh);
2985 memcpy(bh->b_data+offset, data, len);
2986 flush_dcache_page(bh->b_page);
2987 unlock_buffer(bh);
2988 if (journal_quota)
2989 err = ext3_journal_dirty_metadata(handle, bh);
2990 else {
2991 /* Always do at least ordered writes for quotas */
2992 err = ext3_journal_dirty_data(handle, bh);
2993 mark_buffer_dirty(bh);
2994 }
2995 brelse(bh);
2997out: 2996out:
2998 if (len == towrite) { 2997 if (err) {
2999 mutex_unlock(&inode->i_mutex); 2998 mutex_unlock(&inode->i_mutex);
3000 return err; 2999 return err;
3001 } 3000 }
3002 if (inode->i_size < off+len-towrite) { 3001 if (inode->i_size < off + len) {
3003 i_size_write(inode, off+len-towrite); 3002 i_size_write(inode, off + len);
3004 EXT3_I(inode)->i_disksize = inode->i_size; 3003 EXT3_I(inode)->i_disksize = inode->i_size;
3005 } 3004 }
3006 inode->i_version++; 3005 inode->i_version++;
3007 inode->i_mtime = inode->i_ctime = CURRENT_TIME; 3006 inode->i_mtime = inode->i_ctime = CURRENT_TIME;
3008 ext3_mark_inode_dirty(handle, inode); 3007 ext3_mark_inode_dirty(handle, inode);
3009 mutex_unlock(&inode->i_mutex); 3008 mutex_unlock(&inode->i_mutex);
3010 return len - towrite; 3009 return len;
3011} 3010}
3012 3011
3013#endif 3012#endif