diff options
| author | Dmitry Monakhov <dmonakhov@openvz.org> | 2010-02-16 11:33:42 -0500 |
|---|---|---|
| committer | Jan Kara <jack@suse.cz> | 2010-03-04 18:20:26 -0500 |
| commit | e5472147e1c0712d95d973acfdbd862957c77add (patch) | |
| tree | a1e48e5e57931e53c3c9991b5005dd911299a71a /fs | |
| parent | ac0e773718dc20551e72900d2e7eada96ac91100 (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>
Diffstat (limited to 'fs')
| -rw-r--r-- | fs/ext3/super.c | 69 |
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); | ||
| 2997 | out: | 2996 | out: |
| 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 |
