Commit ca3cef46 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'ext4_for_linus_stable' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4

Pull ext4 fixes from Ted Ts'o:
 "Fix a number of ext4 bugs in fast_commit, inline data, and delayed
  allocation.

  Also fix error handling code paths in ext4_dx_readdir() and
  ext4_fill_super().

  Finally, avoid a grabbing a journal head in the delayed allocation
  write in the common cases where we are overwriting a pre-existing
  block or appending to an inode"

* tag 'ext4_for_linus_stable' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4:
  ext4: recheck buffer uptodate bit under buffer lock
  ext4: fix potential infinite loop in ext4_dx_readdir()
  ext4: flush s_error_work before journal destroy in ext4_fill_super
  ext4: fix loff_t overflow in ext4_max_bitmap_size()
  ext4: fix reserved space counter leakage
  ext4: limit the number of blocks in one ADD_RANGE TLV
  ext4: enforce buffer head state assertion in ext4_da_map_blocks
  ext4: remove extent cache entries when truncating inline data
  ext4: drop unnecessary journal handle in delalloc write
  ext4: factor out write end code of inline file
  ext4: correct the error path of ext4_write_inline_data_end()
  ext4: check and update i_disksize properly
  ext4: add error checking to ext4_ext_replay_set_iblocks()
parents 7fab1c12 f2c77973
...@@ -551,7 +551,7 @@ static int ext4_dx_readdir(struct file *file, struct dir_context *ctx) ...@@ -551,7 +551,7 @@ static int ext4_dx_readdir(struct file *file, struct dir_context *ctx)
struct dir_private_info *info = file->private_data; struct dir_private_info *info = file->private_data;
struct inode *inode = file_inode(file); struct inode *inode = file_inode(file);
struct fname *fname; struct fname *fname;
int ret; int ret = 0;
if (!info) { if (!info) {
info = ext4_htree_create_dir_info(file, ctx->pos); info = ext4_htree_create_dir_info(file, ctx->pos);
...@@ -599,7 +599,7 @@ static int ext4_dx_readdir(struct file *file, struct dir_context *ctx) ...@@ -599,7 +599,7 @@ static int ext4_dx_readdir(struct file *file, struct dir_context *ctx)
info->curr_minor_hash, info->curr_minor_hash,
&info->next_hash); &info->next_hash);
if (ret < 0) if (ret < 0)
return ret; goto finished;
if (ret == 0) { if (ret == 0) {
ctx->pos = ext4_get_htree_eof(file); ctx->pos = ext4_get_htree_eof(file);
break; break;
...@@ -630,7 +630,7 @@ static int ext4_dx_readdir(struct file *file, struct dir_context *ctx) ...@@ -630,7 +630,7 @@ static int ext4_dx_readdir(struct file *file, struct dir_context *ctx)
} }
finished: finished:
info->last_pos = ctx->pos; info->last_pos = ctx->pos;
return 0; return ret < 0 ? ret : 0;
} }
static int ext4_release_dir(struct inode *inode, struct file *filp) static int ext4_release_dir(struct inode *inode, struct file *filp)
......
...@@ -3593,9 +3593,6 @@ extern int ext4_da_write_inline_data_begin(struct address_space *mapping, ...@@ -3593,9 +3593,6 @@ extern int ext4_da_write_inline_data_begin(struct address_space *mapping,
unsigned flags, unsigned flags,
struct page **pagep, struct page **pagep,
void **fsdata); void **fsdata);
extern int ext4_da_write_inline_data_end(struct inode *inode, loff_t pos,
unsigned len, unsigned copied,
struct page *page);
extern int ext4_try_add_inline_entry(handle_t *handle, extern int ext4_try_add_inline_entry(handle_t *handle,
struct ext4_filename *fname, struct ext4_filename *fname,
struct inode *dir, struct inode *inode); struct inode *dir, struct inode *inode);
......
...@@ -5916,7 +5916,7 @@ void ext4_ext_replay_shrink_inode(struct inode *inode, ext4_lblk_t end) ...@@ -5916,7 +5916,7 @@ void ext4_ext_replay_shrink_inode(struct inode *inode, ext4_lblk_t end)
} }
/* Check if *cur is a hole and if it is, skip it */ /* Check if *cur is a hole and if it is, skip it */
static void skip_hole(struct inode *inode, ext4_lblk_t *cur) static int skip_hole(struct inode *inode, ext4_lblk_t *cur)
{ {
int ret; int ret;
struct ext4_map_blocks map; struct ext4_map_blocks map;
...@@ -5925,9 +5925,12 @@ static void skip_hole(struct inode *inode, ext4_lblk_t *cur) ...@@ -5925,9 +5925,12 @@ static void skip_hole(struct inode *inode, ext4_lblk_t *cur)
map.m_len = ((inode->i_size) >> inode->i_sb->s_blocksize_bits) - *cur; map.m_len = ((inode->i_size) >> inode->i_sb->s_blocksize_bits) - *cur;
ret = ext4_map_blocks(NULL, inode, &map, 0); ret = ext4_map_blocks(NULL, inode, &map, 0);
if (ret < 0)
return ret;
if (ret != 0) if (ret != 0)
return; return 0;
*cur = *cur + map.m_len; *cur = *cur + map.m_len;
return 0;
} }
/* Count number of blocks used by this inode and update i_blocks */ /* Count number of blocks used by this inode and update i_blocks */
...@@ -5976,7 +5979,9 @@ int ext4_ext_replay_set_iblocks(struct inode *inode) ...@@ -5976,7 +5979,9 @@ int ext4_ext_replay_set_iblocks(struct inode *inode)
* iblocks by total number of differences found. * iblocks by total number of differences found.
*/ */
cur = 0; cur = 0;
skip_hole(inode, &cur); ret = skip_hole(inode, &cur);
if (ret < 0)
goto out;
path = ext4_find_extent(inode, cur, NULL, 0); path = ext4_find_extent(inode, cur, NULL, 0);
if (IS_ERR(path)) if (IS_ERR(path))
goto out; goto out;
...@@ -5995,8 +6000,12 @@ int ext4_ext_replay_set_iblocks(struct inode *inode) ...@@ -5995,8 +6000,12 @@ int ext4_ext_replay_set_iblocks(struct inode *inode)
} }
cur = max(cur + 1, le32_to_cpu(ex->ee_block) + cur = max(cur + 1, le32_to_cpu(ex->ee_block) +
ext4_ext_get_actual_len(ex)); ext4_ext_get_actual_len(ex));
skip_hole(inode, &cur); ret = skip_hole(inode, &cur);
if (ret < 0) {
ext4_ext_drop_refs(path);
kfree(path);
break;
}
path2 = ext4_find_extent(inode, cur, NULL, 0); path2 = ext4_find_extent(inode, cur, NULL, 0);
if (IS_ERR(path2)) { if (IS_ERR(path2)) {
ext4_ext_drop_refs(path); ext4_ext_drop_refs(path);
......
...@@ -892,6 +892,12 @@ static int ext4_fc_write_inode_data(struct inode *inode, u32 *crc) ...@@ -892,6 +892,12 @@ static int ext4_fc_write_inode_data(struct inode *inode, u32 *crc)
sizeof(lrange), (u8 *)&lrange, crc)) sizeof(lrange), (u8 *)&lrange, crc))
return -ENOSPC; return -ENOSPC;
} else { } else {
unsigned int max = (map.m_flags & EXT4_MAP_UNWRITTEN) ?
EXT_UNWRITTEN_MAX_LEN : EXT_INIT_MAX_LEN;
/* Limit the number of blocks in one extent */
map.m_len = min(max, map.m_len);
fc_ext.fc_ino = cpu_to_le32(inode->i_ino); fc_ext.fc_ino = cpu_to_le32(inode->i_ino);
ex = (struct ext4_extent *)&fc_ext.fc_ex; ex = (struct ext4_extent *)&fc_ext.fc_ex;
ex->ee_block = cpu_to_le32(map.m_lblk); ex->ee_block = cpu_to_le32(map.m_lblk);
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <linux/iomap.h> #include <linux/iomap.h>
#include <linux/fiemap.h> #include <linux/fiemap.h>
#include <linux/iversion.h> #include <linux/iversion.h>
#include <linux/backing-dev.h>
#include "ext4_jbd2.h" #include "ext4_jbd2.h"
#include "ext4.h" #include "ext4.h"
...@@ -733,35 +734,35 @@ int ext4_try_to_write_inline_data(struct address_space *mapping, ...@@ -733,35 +734,35 @@ int ext4_try_to_write_inline_data(struct address_space *mapping,
int ext4_write_inline_data_end(struct inode *inode, loff_t pos, unsigned len, int ext4_write_inline_data_end(struct inode *inode, loff_t pos, unsigned len,
unsigned copied, struct page *page) unsigned copied, struct page *page)
{ {
int ret, no_expand; handle_t *handle = ext4_journal_current_handle();
int no_expand;
void *kaddr; void *kaddr;
struct ext4_iloc iloc; struct ext4_iloc iloc;
int ret = 0, ret2;
if (unlikely(copied < len)) { if (unlikely(copied < len) && !PageUptodate(page))
if (!PageUptodate(page)) {
copied = 0; copied = 0;
goto out;
}
}
if (likely(copied)) {
ret = ext4_get_inode_loc(inode, &iloc); ret = ext4_get_inode_loc(inode, &iloc);
if (ret) { if (ret) {
unlock_page(page);
put_page(page);
ext4_std_error(inode->i_sb, ret); ext4_std_error(inode->i_sb, ret);
copied = 0;
goto out; goto out;
} }
ext4_write_lock_xattr(inode, &no_expand); ext4_write_lock_xattr(inode, &no_expand);
BUG_ON(!ext4_has_inline_data(inode)); BUG_ON(!ext4_has_inline_data(inode));
/* /*
* ei->i_inline_off may have changed since ext4_write_begin() * ei->i_inline_off may have changed since
* called ext4_try_to_write_inline_data() * ext4_write_begin() called
* ext4_try_to_write_inline_data()
*/ */
(void) ext4_find_inline_data_nolock(inode); (void) ext4_find_inline_data_nolock(inode);
kaddr = kmap_atomic(page); kaddr = kmap_atomic(page);
ext4_write_inline_data(inode, &iloc, kaddr, pos, len); ext4_write_inline_data(inode, &iloc, kaddr, pos, copied);
kunmap_atomic(kaddr); kunmap_atomic(kaddr);
SetPageUptodate(page); SetPageUptodate(page);
/* clear page dirty so that writepages wouldn't work for us. */ /* clear page dirty so that writepages wouldn't work for us. */
...@@ -769,9 +770,47 @@ int ext4_write_inline_data_end(struct inode *inode, loff_t pos, unsigned len, ...@@ -769,9 +770,47 @@ int ext4_write_inline_data_end(struct inode *inode, loff_t pos, unsigned len,
ext4_write_unlock_xattr(inode, &no_expand); ext4_write_unlock_xattr(inode, &no_expand);
brelse(iloc.bh); brelse(iloc.bh);
/*
* It's important to update i_size while still holding page
* lock: page writeout could otherwise come in and zero
* beyond i_size.
*/
ext4_update_inode_size(inode, pos + copied);
}
unlock_page(page);
put_page(page);
/*
* Don't mark the inode dirty under page lock. First, it unnecessarily
* makes the holding time of page lock longer. Second, it forces lock
* ordering of page lock and transaction start for journaling
* filesystems.
*/
if (likely(copied))
mark_inode_dirty(inode); mark_inode_dirty(inode);
out: out:
return copied; /*
* If we didn't copy as much data as expected, we need to trim back
* size of xattr containing inline data.
*/
if (pos + len > inode->i_size && ext4_can_truncate(inode))
ext4_orphan_add(handle, inode);
ret2 = ext4_journal_stop(handle);
if (!ret)
ret = ret2;
if (pos + len > inode->i_size) {
ext4_truncate_failed_write(inode);
/*
* If truncate failed early the inode might still be
* on the orphan list; we need to make sure the inode
* is removed from the orphan list in that case.
*/
if (inode->i_nlink)
ext4_orphan_del(NULL, inode);
}
return ret ? ret : copied;
} }
struct buffer_head * struct buffer_head *
...@@ -953,43 +992,6 @@ int ext4_da_write_inline_data_begin(struct address_space *mapping, ...@@ -953,43 +992,6 @@ int ext4_da_write_inline_data_begin(struct address_space *mapping,
return ret; return ret;
} }
int ext4_da_write_inline_data_end(struct inode *inode, loff_t pos,
unsigned len, unsigned copied,
struct page *page)
{
int ret;
ret = ext4_write_inline_data_end(inode, pos, len, copied, page);
if (ret < 0) {
unlock_page(page);
put_page(page);
return ret;
}
copied = ret;
/*
* No need to use i_size_read() here, the i_size
* cannot change under us because we hold i_mutex.
*
* But it's important to update i_size while still holding page lock:
* page writeout could otherwise come in and zero beyond i_size.
*/
if (pos+copied > inode->i_size)
i_size_write(inode, pos+copied);
unlock_page(page);
put_page(page);
/*
* Don't mark the inode dirty under page lock. First, it unnecessarily
* makes the holding time of page lock longer. Second, it forces lock
* ordering of page lock and transaction start for journaling
* filesystems.
*/
mark_inode_dirty(inode);
return copied;
}
#ifdef INLINE_DIR_DEBUG #ifdef INLINE_DIR_DEBUG
void ext4_show_inline_dir(struct inode *dir, struct buffer_head *bh, void ext4_show_inline_dir(struct inode *dir, struct buffer_head *bh,
void *inline_start, int inline_size) void *inline_start, int inline_size)
...@@ -1917,6 +1919,24 @@ int ext4_inline_data_truncate(struct inode *inode, int *has_inline) ...@@ -1917,6 +1919,24 @@ int ext4_inline_data_truncate(struct inode *inode, int *has_inline)
EXT4_I(inode)->i_disksize = i_size; EXT4_I(inode)->i_disksize = i_size;
if (i_size < inline_size) { if (i_size < inline_size) {
/*
* if there's inline data to truncate and this file was
* converted to extents after that inline data was written,
* the extent status cache must be cleared to avoid leaving
* behind stale delayed allocated extent entries
*/
if (!ext4_test_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA)) {
retry:
err = ext4_es_remove_extent(inode, 0, EXT_MAX_BLOCKS);
if (err == -ENOMEM) {
cond_resched();
congestion_wait(BLK_RW_ASYNC, HZ/50);
goto retry;
}
if (err)
goto out_error;
}
/* Clear the content in the xattr space. */ /* Clear the content in the xattr space. */
if (inline_size > EXT4_MIN_INLINE_DATA_SIZE) { if (inline_size > EXT4_MIN_INLINE_DATA_SIZE) {
if ((err = ext4_xattr_ibody_find(inode, &i, &is)) != 0) if ((err = ext4_xattr_ibody_find(inode, &i, &is)) != 0)
......
This diff is collapsed.
...@@ -658,7 +658,7 @@ static void ext4_handle_error(struct super_block *sb, bool force_ro, int error, ...@@ -658,7 +658,7 @@ static void ext4_handle_error(struct super_block *sb, bool force_ro, int error,
* constraints, it may not be safe to do it right here so we * constraints, it may not be safe to do it right here so we
* defer superblock flushing to a workqueue. * defer superblock flushing to a workqueue.
*/ */
if (continue_fs) if (continue_fs && journal)
schedule_work(&EXT4_SB(sb)->s_error_work); schedule_work(&EXT4_SB(sb)->s_error_work);
else else
ext4_commit_super(sb); ext4_commit_super(sb);
...@@ -1350,6 +1350,12 @@ static void ext4_destroy_inode(struct inode *inode) ...@@ -1350,6 +1350,12 @@ static void ext4_destroy_inode(struct inode *inode)
true); true);
dump_stack(); dump_stack();
} }
if (EXT4_I(inode)->i_reserved_data_blocks)
ext4_msg(inode->i_sb, KERN_ERR,
"Inode %lu (%p): i_reserved_data_blocks (%u) not cleared!",
inode->i_ino, EXT4_I(inode),
EXT4_I(inode)->i_reserved_data_blocks);
} }
static void init_once(void *foo) static void init_once(void *foo)
...@@ -3021,17 +3027,17 @@ static loff_t ext4_max_size(int blkbits, int has_huge_files) ...@@ -3021,17 +3027,17 @@ static loff_t ext4_max_size(int blkbits, int has_huge_files)
*/ */
static loff_t ext4_max_bitmap_size(int bits, int has_huge_files) static loff_t ext4_max_bitmap_size(int bits, int has_huge_files)
{ {
loff_t res = EXT4_NDIR_BLOCKS; unsigned long long upper_limit, res = EXT4_NDIR_BLOCKS;
int meta_blocks; int meta_blocks;
loff_t upper_limit;
/* This is calculated to be the largest file size for a dense, block /*
* This is calculated to be the largest file size for a dense, block
* mapped file such that the file's total number of 512-byte sectors, * mapped file such that the file's total number of 512-byte sectors,
* including data and all indirect blocks, does not exceed (2^48 - 1). * including data and all indirect blocks, does not exceed (2^48 - 1).
* *
* __u32 i_blocks_lo and _u16 i_blocks_high represent the total * __u32 i_blocks_lo and _u16 i_blocks_high represent the total
* number of 512-byte sectors of the file. * number of 512-byte sectors of the file.
*/ */
if (!has_huge_files) { if (!has_huge_files) {
/* /*
* !has_huge_files or implies that the inode i_block field * !has_huge_files or implies that the inode i_block field
...@@ -3074,7 +3080,7 @@ static loff_t ext4_max_bitmap_size(int bits, int has_huge_files) ...@@ -3074,7 +3080,7 @@ static loff_t ext4_max_bitmap_size(int bits, int has_huge_files)
if (res > MAX_LFS_FILESIZE) if (res > MAX_LFS_FILESIZE)
res = MAX_LFS_FILESIZE; res = MAX_LFS_FILESIZE;
return res; return (loff_t)res;
} }
static ext4_fsblk_t descriptor_loc(struct super_block *sb, static ext4_fsblk_t descriptor_loc(struct super_block *sb,
...@@ -5042,12 +5048,15 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) ...@@ -5042,12 +5048,15 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
sbi->s_ea_block_cache = NULL; sbi->s_ea_block_cache = NULL;
if (sbi->s_journal) { if (sbi->s_journal) {
/* flush s_error_work before journal destroy. */
flush_work(&sbi->s_error_work);
jbd2_journal_destroy(sbi->s_journal); jbd2_journal_destroy(sbi->s_journal);
sbi->s_journal = NULL; sbi->s_journal = NULL;
} }
failed_mount3a: failed_mount3a:
ext4_es_unregister_shrinker(sbi); ext4_es_unregister_shrinker(sbi);
failed_mount3: failed_mount3:
/* flush s_error_work before sbi destroy */
flush_work(&sbi->s_error_work); flush_work(&sbi->s_error_work);
del_timer_sync(&sbi->s_err_report); del_timer_sync(&sbi->s_err_report);
ext4_stop_mmpd(sbi); ext4_stop_mmpd(sbi);
......
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