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 an pre-existing block or appending to an inode. -----BEGIN PGP SIGNATURE----- iQEzBAABCAAdFiEEK2m5VNv+CHkogTfJ8vlZVpUNgaMFAmFZ2SsACgkQ8vlZVpUN gaN6DAgAkIeisL1EfQT0VwshEs8y7N6IoX8dydLSRLpNf5oWiJOv2CTY9Qpi6X/C qNfuLsbJ2NXChvhIAM2hD82hvX21rYc6iqPxgho02VF4eYIP7NzLjwTFKnKbHPB5 TiF498nJTnkcmSrJUEXmSAEdLoCwa5THH9+9HVHXZrkLXPULBtOOJ85mDAcIzVhV Zqb7yfbpWl0gnF0S0YjNATPtbhcC9EiC4MOVYVesRlgT9B3+k5q4fmVU0euTU9OH F2H6TNG+Mg/19gTnDP5acB9+eXHvYEqMpe+CaDifR9iFE9PTG/Edhxr6z9roXhHr kBvEVHSFH+YTEJXghnpS9YDd9Lwc9w== =WKzd -----END PGP SIGNATURE----- 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()
This commit is contained in:
Коммит
ca3cef466f
|
@ -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 inode *inode = file_inode(file);
|
||||
struct fname *fname;
|
||||
int ret;
|
||||
int ret = 0;
|
||||
|
||||
if (!info) {
|
||||
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)
|
|||
info->curr_minor_hash,
|
||||
&info->next_hash);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto finished;
|
||||
if (ret == 0) {
|
||||
ctx->pos = ext4_get_htree_eof(file);
|
||||
break;
|
||||
|
@ -630,7 +630,7 @@ static int ext4_dx_readdir(struct file *file, struct dir_context *ctx)
|
|||
}
|
||||
finished:
|
||||
info->last_pos = ctx->pos;
|
||||
return 0;
|
||||
return ret < 0 ? ret : 0;
|
||||
}
|
||||
|
||||
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,
|
|||
unsigned flags,
|
||||
struct page **pagep,
|
||||
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,
|
||||
struct ext4_filename *fname,
|
||||
struct inode *dir, struct inode *inode);
|
||||
|
|
|
@ -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 */
|
||||
static void skip_hole(struct inode *inode, ext4_lblk_t *cur)
|
||||
static int skip_hole(struct inode *inode, ext4_lblk_t *cur)
|
||||
{
|
||||
int ret;
|
||||
struct ext4_map_blocks map;
|
||||
|
@ -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;
|
||||
|
||||
ret = ext4_map_blocks(NULL, inode, &map, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret != 0)
|
||||
return;
|
||||
return 0;
|
||||
*cur = *cur + map.m_len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 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)
|
|||
* iblocks by total number of differences found.
|
||||
*/
|
||||
cur = 0;
|
||||
skip_hole(inode, &cur);
|
||||
ret = skip_hole(inode, &cur);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
path = ext4_find_extent(inode, cur, NULL, 0);
|
||||
if (IS_ERR(path))
|
||||
goto out;
|
||||
|
@ -5995,8 +6000,12 @@ int ext4_ext_replay_set_iblocks(struct inode *inode)
|
|||
}
|
||||
cur = max(cur + 1, le32_to_cpu(ex->ee_block) +
|
||||
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);
|
||||
if (IS_ERR(path2)) {
|
||||
ext4_ext_drop_refs(path);
|
||||
|
|
|
@ -892,6 +892,12 @@ static int ext4_fc_write_inode_data(struct inode *inode, u32 *crc)
|
|||
sizeof(lrange), (u8 *)&lrange, crc))
|
||||
return -ENOSPC;
|
||||
} 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);
|
||||
ex = (struct ext4_extent *)&fc_ext.fc_ex;
|
||||
ex->ee_block = cpu_to_le32(map.m_lblk);
|
||||
|
|
150
fs/ext4/inline.c
150
fs/ext4/inline.c
|
@ -7,6 +7,7 @@
|
|||
#include <linux/iomap.h>
|
||||
#include <linux/fiemap.h>
|
||||
#include <linux/iversion.h>
|
||||
#include <linux/backing-dev.h>
|
||||
|
||||
#include "ext4_jbd2.h"
|
||||
#include "ext4.h"
|
||||
|
@ -733,45 +734,83 @@ convert:
|
|||
int ext4_write_inline_data_end(struct inode *inode, loff_t pos, unsigned len,
|
||||
unsigned copied, struct page *page)
|
||||
{
|
||||
int ret, no_expand;
|
||||
handle_t *handle = ext4_journal_current_handle();
|
||||
int no_expand;
|
||||
void *kaddr;
|
||||
struct ext4_iloc iloc;
|
||||
int ret = 0, ret2;
|
||||
|
||||
if (unlikely(copied < len)) {
|
||||
if (!PageUptodate(page)) {
|
||||
copied = 0;
|
||||
if (unlikely(copied < len) && !PageUptodate(page))
|
||||
copied = 0;
|
||||
|
||||
if (likely(copied)) {
|
||||
ret = ext4_get_inode_loc(inode, &iloc);
|
||||
if (ret) {
|
||||
unlock_page(page);
|
||||
put_page(page);
|
||||
ext4_std_error(inode->i_sb, ret);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
ext4_write_lock_xattr(inode, &no_expand);
|
||||
BUG_ON(!ext4_has_inline_data(inode));
|
||||
|
||||
ret = ext4_get_inode_loc(inode, &iloc);
|
||||
if (ret) {
|
||||
ext4_std_error(inode->i_sb, ret);
|
||||
copied = 0;
|
||||
goto out;
|
||||
}
|
||||
/*
|
||||
* ei->i_inline_off may have changed since
|
||||
* ext4_write_begin() called
|
||||
* ext4_try_to_write_inline_data()
|
||||
*/
|
||||
(void) ext4_find_inline_data_nolock(inode);
|
||||
|
||||
ext4_write_lock_xattr(inode, &no_expand);
|
||||
BUG_ON(!ext4_has_inline_data(inode));
|
||||
kaddr = kmap_atomic(page);
|
||||
ext4_write_inline_data(inode, &iloc, kaddr, pos, copied);
|
||||
kunmap_atomic(kaddr);
|
||||
SetPageUptodate(page);
|
||||
/* clear page dirty so that writepages wouldn't work for us. */
|
||||
ClearPageDirty(page);
|
||||
|
||||
ext4_write_unlock_xattr(inode, &no_expand);
|
||||
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);
|
||||
|
||||
/*
|
||||
* ei->i_inline_off may have changed since ext4_write_begin()
|
||||
* called ext4_try_to_write_inline_data()
|
||||
* 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.
|
||||
*/
|
||||
(void) ext4_find_inline_data_nolock(inode);
|
||||
|
||||
kaddr = kmap_atomic(page);
|
||||
ext4_write_inline_data(inode, &iloc, kaddr, pos, len);
|
||||
kunmap_atomic(kaddr);
|
||||
SetPageUptodate(page);
|
||||
/* clear page dirty so that writepages wouldn't work for us. */
|
||||
ClearPageDirty(page);
|
||||
|
||||
ext4_write_unlock_xattr(inode, &no_expand);
|
||||
brelse(iloc.bh);
|
||||
mark_inode_dirty(inode);
|
||||
if (likely(copied))
|
||||
mark_inode_dirty(inode);
|
||||
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 *
|
||||
|
@ -953,43 +992,6 @@ out:
|
|||
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
|
||||
void ext4_show_inline_dir(struct inode *dir, struct buffer_head *bh,
|
||||
void *inline_start, int inline_size)
|
||||
|
@ -1917,6 +1919,24 @@ int ext4_inline_data_truncate(struct inode *inode, int *has_inline)
|
|||
EXT4_I(inode)->i_disksize = i_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. */
|
||||
if (inline_size > EXT4_MIN_INLINE_DATA_SIZE) {
|
||||
if ((err = ext4_xattr_ibody_find(inode, &i, &is)) != 0)
|
||||
|
|
178
fs/ext4/inode.c
178
fs/ext4/inode.c
|
@ -1284,22 +1284,14 @@ static int ext4_write_end(struct file *file,
|
|||
loff_t old_size = inode->i_size;
|
||||
int ret = 0, ret2;
|
||||
int i_size_changed = 0;
|
||||
int inline_data = ext4_has_inline_data(inode);
|
||||
bool verity = ext4_verity_in_progress(inode);
|
||||
|
||||
trace_ext4_write_end(inode, pos, len, copied);
|
||||
if (inline_data) {
|
||||
ret = ext4_write_inline_data_end(inode, pos, len,
|
||||
copied, page);
|
||||
if (ret < 0) {
|
||||
unlock_page(page);
|
||||
put_page(page);
|
||||
goto errout;
|
||||
}
|
||||
copied = ret;
|
||||
} else
|
||||
copied = block_write_end(file, mapping, pos,
|
||||
len, copied, page, fsdata);
|
||||
|
||||
if (ext4_has_inline_data(inode))
|
||||
return ext4_write_inline_data_end(inode, pos, len, copied, page);
|
||||
|
||||
copied = block_write_end(file, mapping, pos, len, copied, page, fsdata);
|
||||
/*
|
||||
* it's important to update i_size while still holding page lock:
|
||||
* page writeout could otherwise come in and zero beyond i_size.
|
||||
|
@ -1320,7 +1312,7 @@ static int ext4_write_end(struct file *file,
|
|||
* ordering of page lock and transaction start for journaling
|
||||
* filesystems.
|
||||
*/
|
||||
if (i_size_changed || inline_data)
|
||||
if (i_size_changed)
|
||||
ret = ext4_mark_inode_dirty(handle, inode);
|
||||
|
||||
if (pos + len > inode->i_size && !verity && ext4_can_truncate(inode))
|
||||
|
@ -1329,7 +1321,7 @@ static int ext4_write_end(struct file *file,
|
|||
* inode->i_size. So truncate them
|
||||
*/
|
||||
ext4_orphan_add(handle, inode);
|
||||
errout:
|
||||
|
||||
ret2 = ext4_journal_stop(handle);
|
||||
if (!ret)
|
||||
ret = ret2;
|
||||
|
@ -1395,7 +1387,6 @@ static int ext4_journalled_write_end(struct file *file,
|
|||
int partial = 0;
|
||||
unsigned from, to;
|
||||
int size_changed = 0;
|
||||
int inline_data = ext4_has_inline_data(inode);
|
||||
bool verity = ext4_verity_in_progress(inode);
|
||||
|
||||
trace_ext4_journalled_write_end(inode, pos, len, copied);
|
||||
|
@ -1404,16 +1395,10 @@ static int ext4_journalled_write_end(struct file *file,
|
|||
|
||||
BUG_ON(!ext4_handle_valid(handle));
|
||||
|
||||
if (inline_data) {
|
||||
ret = ext4_write_inline_data_end(inode, pos, len,
|
||||
copied, page);
|
||||
if (ret < 0) {
|
||||
unlock_page(page);
|
||||
put_page(page);
|
||||
goto errout;
|
||||
}
|
||||
copied = ret;
|
||||
} else if (unlikely(copied < len) && !PageUptodate(page)) {
|
||||
if (ext4_has_inline_data(inode))
|
||||
return ext4_write_inline_data_end(inode, pos, len, copied, page);
|
||||
|
||||
if (unlikely(copied < len) && !PageUptodate(page)) {
|
||||
copied = 0;
|
||||
ext4_journalled_zero_new_buffers(handle, inode, page, from, to);
|
||||
} else {
|
||||
|
@ -1436,7 +1421,7 @@ static int ext4_journalled_write_end(struct file *file,
|
|||
if (old_size < pos && !verity)
|
||||
pagecache_isize_extended(inode, old_size, pos);
|
||||
|
||||
if (size_changed || inline_data) {
|
||||
if (size_changed) {
|
||||
ret2 = ext4_mark_inode_dirty(handle, inode);
|
||||
if (!ret)
|
||||
ret = ret2;
|
||||
|
@ -1449,7 +1434,6 @@ static int ext4_journalled_write_end(struct file *file,
|
|||
*/
|
||||
ext4_orphan_add(handle, inode);
|
||||
|
||||
errout:
|
||||
ret2 = ext4_journal_stop(handle);
|
||||
if (!ret)
|
||||
ret = ret2;
|
||||
|
@ -1644,6 +1628,7 @@ static int ext4_insert_delayed_block(struct inode *inode, ext4_lblk_t lblk)
|
|||
struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
|
||||
int ret;
|
||||
bool allocated = false;
|
||||
bool reserved = false;
|
||||
|
||||
/*
|
||||
* If the cluster containing lblk is shared with a delayed,
|
||||
|
@ -1660,6 +1645,7 @@ static int ext4_insert_delayed_block(struct inode *inode, ext4_lblk_t lblk)
|
|||
ret = ext4_da_reserve_space(inode);
|
||||
if (ret != 0) /* ENOSPC */
|
||||
goto errout;
|
||||
reserved = true;
|
||||
} else { /* bigalloc */
|
||||
if (!ext4_es_scan_clu(inode, &ext4_es_is_delonly, lblk)) {
|
||||
if (!ext4_es_scan_clu(inode,
|
||||
|
@ -1672,6 +1658,7 @@ static int ext4_insert_delayed_block(struct inode *inode, ext4_lblk_t lblk)
|
|||
ret = ext4_da_reserve_space(inode);
|
||||
if (ret != 0) /* ENOSPC */
|
||||
goto errout;
|
||||
reserved = true;
|
||||
} else {
|
||||
allocated = true;
|
||||
}
|
||||
|
@ -1682,6 +1669,8 @@ static int ext4_insert_delayed_block(struct inode *inode, ext4_lblk_t lblk)
|
|||
}
|
||||
|
||||
ret = ext4_es_insert_delayed_block(inode, lblk, allocated);
|
||||
if (ret && reserved)
|
||||
ext4_da_release_space(inode, 1);
|
||||
|
||||
errout:
|
||||
return ret;
|
||||
|
@ -1722,13 +1711,16 @@ static int ext4_da_map_blocks(struct inode *inode, sector_t iblock,
|
|||
}
|
||||
|
||||
/*
|
||||
* Delayed extent could be allocated by fallocate.
|
||||
* So we need to check it.
|
||||
* the buffer head associated with a delayed and not unwritten
|
||||
* block found in the extent status cache must contain an
|
||||
* invalid block number and have its BH_New and BH_Delay bits
|
||||
* set, reflecting the state assigned when the block was
|
||||
* initially delayed allocated
|
||||
*/
|
||||
if (ext4_es_is_delayed(&es) && !ext4_es_is_unwritten(&es)) {
|
||||
map_bh(bh, inode->i_sb, invalid_block);
|
||||
set_buffer_new(bh);
|
||||
set_buffer_delay(bh);
|
||||
if (ext4_es_is_delonly(&es)) {
|
||||
BUG_ON(bh->b_blocknr != invalid_block);
|
||||
BUG_ON(!buffer_new(bh));
|
||||
BUG_ON(!buffer_delay(bh));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -2932,19 +2924,6 @@ static int ext4_nonda_switch(struct super_block *sb)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* We always reserve for an inode update; the superblock could be there too */
|
||||
static int ext4_da_write_credits(struct inode *inode, loff_t pos, unsigned len)
|
||||
{
|
||||
if (likely(ext4_has_feature_large_file(inode->i_sb)))
|
||||
return 1;
|
||||
|
||||
if (pos + len <= 0x7fffffffULL)
|
||||
return 1;
|
||||
|
||||
/* We might need to update the superblock to set LARGE_FILE */
|
||||
return 2;
|
||||
}
|
||||
|
||||
static int ext4_da_write_begin(struct file *file, struct address_space *mapping,
|
||||
loff_t pos, unsigned len, unsigned flags,
|
||||
struct page **pagep, void **fsdata)
|
||||
|
@ -2953,7 +2932,6 @@ static int ext4_da_write_begin(struct file *file, struct address_space *mapping,
|
|||
struct page *page;
|
||||
pgoff_t index;
|
||||
struct inode *inode = mapping->host;
|
||||
handle_t *handle;
|
||||
|
||||
if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb))))
|
||||
return -EIO;
|
||||
|
@ -2979,41 +2957,11 @@ static int ext4_da_write_begin(struct file *file, struct address_space *mapping,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* grab_cache_page_write_begin() can take a long time if the
|
||||
* system is thrashing due to memory pressure, or if the page
|
||||
* is being written back. So grab it first before we start
|
||||
* the transaction handle. This also allows us to allocate
|
||||
* the page (if needed) without using GFP_NOFS.
|
||||
*/
|
||||
retry_grab:
|
||||
retry:
|
||||
page = grab_cache_page_write_begin(mapping, index, flags);
|
||||
if (!page)
|
||||
return -ENOMEM;
|
||||
unlock_page(page);
|
||||
|
||||
/*
|
||||
* With delayed allocation, we don't log the i_disksize update
|
||||
* if there is delayed block allocation. But we still need
|
||||
* to journalling the i_disksize update if writes to the end
|
||||
* of file which has an already mapped buffer.
|
||||
*/
|
||||
retry_journal:
|
||||
handle = ext4_journal_start(inode, EXT4_HT_WRITE_PAGE,
|
||||
ext4_da_write_credits(inode, pos, len));
|
||||
if (IS_ERR(handle)) {
|
||||
put_page(page);
|
||||
return PTR_ERR(handle);
|
||||
}
|
||||
|
||||
lock_page(page);
|
||||
if (page->mapping != mapping) {
|
||||
/* The page got truncated from under us */
|
||||
unlock_page(page);
|
||||
put_page(page);
|
||||
ext4_journal_stop(handle);
|
||||
goto retry_grab;
|
||||
}
|
||||
/* In case writeback began while the page was unlocked */
|
||||
wait_for_stable_page(page);
|
||||
|
||||
|
@ -3025,20 +2973,18 @@ retry_journal:
|
|||
#endif
|
||||
if (ret < 0) {
|
||||
unlock_page(page);
|
||||
ext4_journal_stop(handle);
|
||||
put_page(page);
|
||||
/*
|
||||
* block_write_begin may have instantiated a few blocks
|
||||
* outside i_size. Trim these off again. Don't need
|
||||
* i_size_read because we hold i_mutex.
|
||||
* i_size_read because we hold inode lock.
|
||||
*/
|
||||
if (pos + len > inode->i_size)
|
||||
ext4_truncate_failed_write(inode);
|
||||
|
||||
if (ret == -ENOSPC &&
|
||||
ext4_should_retry_alloc(inode->i_sb, &retries))
|
||||
goto retry_journal;
|
||||
|
||||
put_page(page);
|
||||
goto retry;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -3075,8 +3021,6 @@ static int ext4_da_write_end(struct file *file,
|
|||
struct page *page, void *fsdata)
|
||||
{
|
||||
struct inode *inode = mapping->host;
|
||||
int ret = 0, ret2;
|
||||
handle_t *handle = ext4_journal_current_handle();
|
||||
loff_t new_i_size;
|
||||
unsigned long start, end;
|
||||
int write_mode = (int)(unsigned long)fsdata;
|
||||
|
@ -3086,44 +3030,36 @@ static int ext4_da_write_end(struct file *file,
|
|||
len, copied, page, fsdata);
|
||||
|
||||
trace_ext4_da_write_end(inode, pos, len, copied);
|
||||
start = pos & (PAGE_SIZE - 1);
|
||||
end = start + copied - 1;
|
||||
|
||||
/*
|
||||
* generic_write_end() will run mark_inode_dirty() if i_size
|
||||
* changes. So let's piggyback the i_disksize mark_inode_dirty
|
||||
* into that.
|
||||
*/
|
||||
new_i_size = pos + copied;
|
||||
if (copied && new_i_size > EXT4_I(inode)->i_disksize) {
|
||||
if (ext4_has_inline_data(inode) ||
|
||||
ext4_da_should_update_i_disksize(page, end)) {
|
||||
ext4_update_i_disksize(inode, new_i_size);
|
||||
/* We need to mark inode dirty even if
|
||||
* new_i_size is less that inode->i_size
|
||||
* bu greater than i_disksize.(hint delalloc)
|
||||
*/
|
||||
ret = ext4_mark_inode_dirty(handle, inode);
|
||||
}
|
||||
}
|
||||
|
||||
if (write_mode != CONVERT_INLINE_DATA &&
|
||||
ext4_test_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA) &&
|
||||
ext4_has_inline_data(inode))
|
||||
ret2 = ext4_da_write_inline_data_end(inode, pos, len, copied,
|
||||
page);
|
||||
else
|
||||
ret2 = generic_write_end(file, mapping, pos, len, copied,
|
||||
page, fsdata);
|
||||
return ext4_write_inline_data_end(inode, pos, len, copied, page);
|
||||
|
||||
copied = ret2;
|
||||
if (ret2 < 0)
|
||||
ret = ret2;
|
||||
ret2 = ext4_journal_stop(handle);
|
||||
if (unlikely(ret2 && !ret))
|
||||
ret = ret2;
|
||||
start = pos & (PAGE_SIZE - 1);
|
||||
end = start + copied - 1;
|
||||
|
||||
return ret ? ret : copied;
|
||||
/*
|
||||
* Since we are holding inode lock, we are sure i_disksize <=
|
||||
* i_size. We also know that if i_disksize < i_size, there are
|
||||
* delalloc writes pending in the range upto i_size. If the end of
|
||||
* the current write is <= i_size, there's no need to touch
|
||||
* i_disksize since writeback will push i_disksize upto i_size
|
||||
* eventually. If the end of the current write is > i_size and
|
||||
* inside an allocated block (ext4_da_should_update_i_disksize()
|
||||
* check), we need to update i_disksize here as neither
|
||||
* ext4_writepage() nor certain ext4_writepages() paths not
|
||||
* allocating blocks update i_disksize.
|
||||
*
|
||||
* Note that we defer inode dirtying to generic_write_end() /
|
||||
* ext4_da_write_inline_data_end().
|
||||
*/
|
||||
new_i_size = pos + copied;
|
||||
if (copied && new_i_size > inode->i_size &&
|
||||
ext4_da_should_update_i_disksize(page, end))
|
||||
ext4_update_i_disksize(inode, new_i_size);
|
||||
|
||||
return generic_write_end(file, mapping, pos, len, copied, page, fsdata);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -4340,6 +4276,12 @@ static int __ext4_get_inode_loc(struct super_block *sb, unsigned long ino,
|
|||
goto has_buffer;
|
||||
|
||||
lock_buffer(bh);
|
||||
if (ext4_buffer_uptodate(bh)) {
|
||||
/* Someone brought it uptodate while we waited */
|
||||
unlock_buffer(bh);
|
||||
goto has_buffer;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we have all information of the inode in memory and this
|
||||
* is the only valid inode in the block, we need not read the
|
||||
|
|
|
@ -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
|
||||
* defer superblock flushing to a workqueue.
|
||||
*/
|
||||
if (continue_fs)
|
||||
if (continue_fs && journal)
|
||||
schedule_work(&EXT4_SB(sb)->s_error_work);
|
||||
else
|
||||
ext4_commit_super(sb);
|
||||
|
@ -1350,6 +1350,12 @@ static void ext4_destroy_inode(struct inode *inode)
|
|||
true);
|
||||
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)
|
||||
|
@ -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)
|
||||
{
|
||||
loff_t res = EXT4_NDIR_BLOCKS;
|
||||
unsigned long long upper_limit, res = EXT4_NDIR_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,
|
||||
* including data and all indirect blocks, does not exceed (2^48 - 1).
|
||||
*
|
||||
* __u32 i_blocks_lo and _u16 i_blocks_high represent the total
|
||||
* number of 512-byte sectors of the file.
|
||||
*/
|
||||
|
||||
if (!has_huge_files) {
|
||||
/*
|
||||
* !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)
|
|||
if (res > MAX_LFS_FILESIZE)
|
||||
res = MAX_LFS_FILESIZE;
|
||||
|
||||
return res;
|
||||
return (loff_t)res;
|
||||
}
|
||||
|
||||
static ext4_fsblk_t descriptor_loc(struct super_block *sb,
|
||||
|
@ -5042,12 +5048,15 @@ failed_mount_wq:
|
|||
sbi->s_ea_block_cache = NULL;
|
||||
|
||||
if (sbi->s_journal) {
|
||||
/* flush s_error_work before journal destroy. */
|
||||
flush_work(&sbi->s_error_work);
|
||||
jbd2_journal_destroy(sbi->s_journal);
|
||||
sbi->s_journal = NULL;
|
||||
}
|
||||
failed_mount3a:
|
||||
ext4_es_unregister_shrinker(sbi);
|
||||
failed_mount3:
|
||||
/* flush s_error_work before sbi destroy */
|
||||
flush_work(&sbi->s_error_work);
|
||||
del_timer_sync(&sbi->s_err_report);
|
||||
ext4_stop_mmpd(sbi);
|
||||
|
|
Загрузка…
Ссылка в новой задаче