Commit 4515c306 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4

* 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4: (47 commits)
  ext4: Fix potential fiemap deadlock (mmap_sem vs. i_data_sem)
  ext4: Do not override ext2 or ext3 if built they are built as modules
  jbd2: Export jbd2_log_start_commit to fix ext4 build
  ext4: Fix insufficient checks in EXT4_IOC_MOVE_EXT
  ext4: Wait for proper transaction commit on fsync
  ext4: fix incorrect block reservation on quota transfer.
  ext4: quota macros cleanup
  ext4: ext4_get_reserved_space() must return bytes instead of blocks
  ext4: remove blocks from inode prealloc list on failure
  ext4: wait for log to commit when umounting
  ext4: Avoid data / filesystem corruption when write fails to copy data
  ext4: Use ext4 file system driver for ext2/ext3 file system mounts
  ext4: Return the PTR_ERR of the correct pointer in setup_new_group_blocks()
  jbd2: Add ENOMEM checking in and for jbd2_journal_write_metadata_buffer()
  ext4: remove unused parameter wbc from __ext4_journalled_writepage()
  ext4: remove encountered_congestion trace
  ext4: move_extent_per_page() cleanup
  ext4: initialize moved_len before calling ext4_move_extents()
  ext4: Fix double-free of blocks with EXT4_IOC_MOVE_EXT
  ext4: use ext4_data_block_valid() in ext4_free_blocks()
  ...
parents a5eba3f6 fab3a549
...@@ -153,8 +153,8 @@ journal_dev=devnum When the external journal device's major/minor numbers ...@@ -153,8 +153,8 @@ journal_dev=devnum When the external journal device's major/minor numbers
identified through its new major/minor numbers encoded identified through its new major/minor numbers encoded
in devnum. in devnum.
noload Don't load the journal on mounting. Note that norecovery Don't load the journal on mounting. Note that
if the filesystem was not unmounted cleanly, noload if the filesystem was not unmounted cleanly,
skipping the journal replay will lead to the skipping the journal replay will lead to the
filesystem containing inconsistencies that can filesystem containing inconsistencies that can
lead to any number of problems. lead to any number of problems.
...@@ -353,6 +353,12 @@ noauto_da_alloc replacing existing files via patterns such as ...@@ -353,6 +353,12 @@ noauto_da_alloc replacing existing files via patterns such as
system crashes before the delayed allocation system crashes before the delayed allocation
blocks are forced to disk. blocks are forced to disk.
discard Controls whether ext4 should issue discard/TRIM
nodiscard(*) commands to the underlying block device when
blocks are freed. This is useful for SSD devices
and sparse/thinly-provisioned LUNs, but it is off
by default until sufficient testing has been done.
Data Mode Data Mode
========= =========
There are 3 different data modes: There are 3 different data modes:
......
...@@ -26,6 +26,16 @@ config EXT4_FS ...@@ -26,6 +26,16 @@ config EXT4_FS
If unsure, say N. If unsure, say N.
config EXT4_USE_FOR_EXT23
bool "Use ext4 for ext2/ext3 file systems"
depends on EXT3_FS=n || EXT2_FS=n
default y
help
Allow the ext4 file system driver code to be used for ext2 or
ext3 file system mounts. This allows users to reduce their
compiled kernel size by using one file system driver for
ext2, ext3, and ext4 file systems.
config EXT4_FS_XATTR config EXT4_FS_XATTR
bool "Ext4 extended attributes" bool "Ext4 extended attributes"
depends on EXT4_FS depends on EXT4_FS
......
...@@ -498,44 +498,6 @@ void ext4_add_groupblocks(handle_t *handle, struct super_block *sb, ...@@ -498,44 +498,6 @@ void ext4_add_groupblocks(handle_t *handle, struct super_block *sb,
return; return;
} }
/**
* ext4_free_blocks() -- Free given blocks and update quota
* @handle: handle for this transaction
* @inode: inode
* @block: start physical block to free
* @count: number of blocks to count
* @metadata: Are these metadata blocks
*/
void ext4_free_blocks(handle_t *handle, struct inode *inode,
ext4_fsblk_t block, unsigned long count,
int metadata)
{
struct super_block *sb;
unsigned long dquot_freed_blocks;
/* this isn't the right place to decide whether block is metadata
* inode.c/extents.c knows better, but for safety ... */
if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))
metadata = 1;
/* We need to make sure we don't reuse
* block released untill the transaction commit.
* writeback mode have weak data consistency so
* don't force data as metadata when freeing block
* for writeback mode.
*/
if (metadata == 0 && !ext4_should_writeback_data(inode))
metadata = 1;
sb = inode->i_sb;
ext4_mb_free_blocks(handle, inode, block, count,
metadata, &dquot_freed_blocks);
if (dquot_freed_blocks)
vfs_dq_free_block(inode, dquot_freed_blocks);
return;
}
/** /**
* ext4_has_free_blocks() * ext4_has_free_blocks()
* @sbi: in-core super block structure. * @sbi: in-core super block structure.
...@@ -761,7 +723,13 @@ static unsigned long ext4_bg_num_gdb_meta(struct super_block *sb, ...@@ -761,7 +723,13 @@ static unsigned long ext4_bg_num_gdb_meta(struct super_block *sb,
static unsigned long ext4_bg_num_gdb_nometa(struct super_block *sb, static unsigned long ext4_bg_num_gdb_nometa(struct super_block *sb,
ext4_group_t group) ext4_group_t group)
{ {
return ext4_bg_has_super(sb, group) ? EXT4_SB(sb)->s_gdb_count : 0; if (!ext4_bg_has_super(sb, group))
return 0;
if (EXT4_HAS_INCOMPAT_FEATURE(sb,EXT4_FEATURE_INCOMPAT_META_BG))
return le32_to_cpu(EXT4_SB(sb)->s_es->s_first_meta_bg);
else
return EXT4_SB(sb)->s_gdb_count;
} }
/** /**
......
...@@ -160,7 +160,7 @@ int ext4_setup_system_zone(struct super_block *sb) ...@@ -160,7 +160,7 @@ int ext4_setup_system_zone(struct super_block *sb)
if (ext4_bg_has_super(sb, i) && if (ext4_bg_has_super(sb, i) &&
((i < 5) || ((i % flex_size) == 0))) ((i < 5) || ((i % flex_size) == 0)))
add_system_zone(sbi, ext4_group_first_block_no(sb, i), add_system_zone(sbi, ext4_group_first_block_no(sb, i),
sbi->s_gdb_count + 1); ext4_bg_num_gdb(sb, i) + 1);
gdp = ext4_get_group_desc(sb, i, NULL); gdp = ext4_get_group_desc(sb, i, NULL);
ret = add_system_zone(sbi, ext4_block_bitmap(sb, gdp), 1); ret = add_system_zone(sbi, ext4_block_bitmap(sb, gdp), 1);
if (ret) if (ret)
...@@ -228,6 +228,7 @@ int ext4_data_block_valid(struct ext4_sb_info *sbi, ext4_fsblk_t start_blk, ...@@ -228,6 +228,7 @@ int ext4_data_block_valid(struct ext4_sb_info *sbi, ext4_fsblk_t start_blk,
struct rb_node *n = sbi->system_blks.rb_node; struct rb_node *n = sbi->system_blks.rb_node;
if ((start_blk <= le32_to_cpu(sbi->s_es->s_first_data_block)) || if ((start_blk <= le32_to_cpu(sbi->s_es->s_first_data_block)) ||
(start_blk + count < start_blk) ||
(start_blk + count > ext4_blocks_count(sbi->s_es))) (start_blk + count > ext4_blocks_count(sbi->s_es)))
return 0; return 0;
while (n) { while (n) {
......
...@@ -375,6 +375,12 @@ struct ext4_new_group_data { ...@@ -375,6 +375,12 @@ struct ext4_new_group_data {
#define EXT4_GET_BLOCKS_DIO_CONVERT_EXT (EXT4_GET_BLOCKS_CONVERT|\ #define EXT4_GET_BLOCKS_DIO_CONVERT_EXT (EXT4_GET_BLOCKS_CONVERT|\
EXT4_GET_BLOCKS_DIO_CREATE_EXT) EXT4_GET_BLOCKS_DIO_CREATE_EXT)
/*
* Flags used by ext4_free_blocks
*/
#define EXT4_FREE_BLOCKS_METADATA 0x0001
#define EXT4_FREE_BLOCKS_FORGET 0x0002
/* /*
* ioctl commands * ioctl commands
*/ */
...@@ -703,6 +709,13 @@ struct ext4_inode_info { ...@@ -703,6 +709,13 @@ struct ext4_inode_info {
struct list_head i_aio_dio_complete_list; struct list_head i_aio_dio_complete_list;
/* current io_end structure for async DIO write*/ /* current io_end structure for async DIO write*/
ext4_io_end_t *cur_aio_dio; ext4_io_end_t *cur_aio_dio;
/*
* Transactions that contain inode's metadata needed to complete
* fsync and fdatasync, respectively.
*/
tid_t i_sync_tid;
tid_t i_datasync_tid;
}; };
/* /*
...@@ -750,6 +763,7 @@ struct ext4_inode_info { ...@@ -750,6 +763,7 @@ struct ext4_inode_info {
#define EXT4_MOUNT_DELALLOC 0x8000000 /* Delalloc support */ #define EXT4_MOUNT_DELALLOC 0x8000000 /* Delalloc support */
#define EXT4_MOUNT_DATA_ERR_ABORT 0x10000000 /* Abort on file data write */ #define EXT4_MOUNT_DATA_ERR_ABORT 0x10000000 /* Abort on file data write */
#define EXT4_MOUNT_BLOCK_VALIDITY 0x20000000 /* Block validity checking */ #define EXT4_MOUNT_BLOCK_VALIDITY 0x20000000 /* Block validity checking */
#define EXT4_MOUNT_DISCARD 0x40000000 /* Issue DISCARD requests */
#define clear_opt(o, opt) o &= ~EXT4_MOUNT_##opt #define clear_opt(o, opt) o &= ~EXT4_MOUNT_##opt
#define set_opt(o, opt) o |= EXT4_MOUNT_##opt #define set_opt(o, opt) o |= EXT4_MOUNT_##opt
...@@ -1324,8 +1338,6 @@ extern ext4_fsblk_t ext4_new_meta_blocks(handle_t *handle, struct inode *inode, ...@@ -1324,8 +1338,6 @@ extern ext4_fsblk_t ext4_new_meta_blocks(handle_t *handle, struct inode *inode,
ext4_fsblk_t goal, unsigned long *count, int *errp); ext4_fsblk_t goal, unsigned long *count, int *errp);
extern int ext4_claim_free_blocks(struct ext4_sb_info *sbi, s64 nblocks); extern int ext4_claim_free_blocks(struct ext4_sb_info *sbi, s64 nblocks);
extern int ext4_has_free_blocks(struct ext4_sb_info *sbi, s64 nblocks); extern int ext4_has_free_blocks(struct ext4_sb_info *sbi, s64 nblocks);
extern void ext4_free_blocks(handle_t *handle, struct inode *inode,
ext4_fsblk_t block, unsigned long count, int metadata);
extern void ext4_add_groupblocks(handle_t *handle, struct super_block *sb, extern void ext4_add_groupblocks(handle_t *handle, struct super_block *sb,
ext4_fsblk_t block, unsigned long count); ext4_fsblk_t block, unsigned long count);
extern ext4_fsblk_t ext4_count_free_blocks(struct super_block *); extern ext4_fsblk_t ext4_count_free_blocks(struct super_block *);
...@@ -1384,16 +1396,15 @@ extern int ext4_mb_reserve_blocks(struct super_block *, int); ...@@ -1384,16 +1396,15 @@ extern int ext4_mb_reserve_blocks(struct super_block *, int);
extern void ext4_discard_preallocations(struct inode *); extern void ext4_discard_preallocations(struct inode *);
extern int __init init_ext4_mballoc(void); extern int __init init_ext4_mballoc(void);
extern void exit_ext4_mballoc(void); extern void exit_ext4_mballoc(void);
extern void ext4_mb_free_blocks(handle_t *, struct inode *, extern void ext4_free_blocks(handle_t *handle, struct inode *inode,
ext4_fsblk_t, unsigned long, int, unsigned long *); struct buffer_head *bh, ext4_fsblk_t block,
unsigned long count, int flags);
extern int ext4_mb_add_groupinfo(struct super_block *sb, extern int ext4_mb_add_groupinfo(struct super_block *sb,
ext4_group_t i, struct ext4_group_desc *desc); ext4_group_t i, struct ext4_group_desc *desc);
extern int ext4_mb_get_buddy_cache_lock(struct super_block *, ext4_group_t); extern int ext4_mb_get_buddy_cache_lock(struct super_block *, ext4_group_t);
extern void ext4_mb_put_buddy_cache_lock(struct super_block *, extern void ext4_mb_put_buddy_cache_lock(struct super_block *,
ext4_group_t, int); ext4_group_t, int);
/* inode.c */ /* inode.c */
int ext4_forget(handle_t *handle, int is_metadata, struct inode *inode,
struct buffer_head *bh, ext4_fsblk_t blocknr);
struct buffer_head *ext4_getblk(handle_t *, struct inode *, struct buffer_head *ext4_getblk(handle_t *, struct inode *,
ext4_lblk_t, int, int *); ext4_lblk_t, int, int *);
struct buffer_head *ext4_bread(handle_t *, struct inode *, struct buffer_head *ext4_bread(handle_t *, struct inode *,
......
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
#include "ext4_jbd2.h" #include "ext4_jbd2.h"
#include <trace/events/ext4.h>
int __ext4_journal_get_undo_access(const char *where, handle_t *handle, int __ext4_journal_get_undo_access(const char *where, handle_t *handle,
struct buffer_head *bh) struct buffer_head *bh)
{ {
...@@ -32,35 +34,69 @@ int __ext4_journal_get_write_access(const char *where, handle_t *handle, ...@@ -32,35 +34,69 @@ int __ext4_journal_get_write_access(const char *where, handle_t *handle,
return err; return err;
} }
int __ext4_journal_forget(const char *where, handle_t *handle, /*
struct buffer_head *bh) * The ext4 forget function must perform a revoke if we are freeing data
* which has been journaled. Metadata (eg. indirect blocks) must be
* revoked in all cases.
*
* "bh" may be NULL: a metadata block may have been freed from memory
* but there may still be a record of it in the journal, and that record
* still needs to be revoked.
*
* If the handle isn't valid we're not journaling, but we still need to
* call into ext4_journal_revoke() to put the buffer head.
*/
int __ext4_forget(const char *where, handle_t *handle, int is_metadata,
struct inode *inode, struct buffer_head *bh,
ext4_fsblk_t blocknr)
{ {
int err = 0; int err;
if (ext4_handle_valid(handle)) { might_sleep();
trace_ext4_forget(inode, is_metadata, blocknr);
BUFFER_TRACE(bh, "enter");
jbd_debug(4, "forgetting bh %p: is_metadata = %d, mode %o, "
"data mode %x\n",
bh, is_metadata, inode->i_mode,
test_opt(inode->i_sb, DATA_FLAGS));
/* In the no journal case, we can just do a bforget and return */
if (!ext4_handle_valid(handle)) {
bforget(bh);
return 0;
}
/* Never use the revoke function if we are doing full data
* journaling: there is no need to, and a V1 superblock won't
* support it. Otherwise, only skip the revoke on un-journaled
* data blocks. */
if (test_opt(inode->i_sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA ||
(!is_metadata && !ext4_should_journal_data(inode))) {
if (bh) {
BUFFER_TRACE(bh, "call jbd2_journal_forget");
err = jbd2_journal_forget(handle, bh); err = jbd2_journal_forget(handle, bh);
if (err) if (err)
ext4_journal_abort_handle(where, __func__, bh, ext4_journal_abort_handle(where, __func__, bh,
handle, err); handle, err);
}
else
bforget(bh);
return err; return err;
} }
return 0;
int __ext4_journal_revoke(const char *where, handle_t *handle, }
ext4_fsblk_t blocknr, struct buffer_head *bh)
{
int err = 0;
if (ext4_handle_valid(handle)) { /*
* data!=journal && (is_metadata || should_journal_data(inode))
*/
BUFFER_TRACE(bh, "call jbd2_journal_revoke");
err = jbd2_journal_revoke(handle, blocknr, bh); err = jbd2_journal_revoke(handle, blocknr, bh);
if (err) if (err) {
ext4_journal_abort_handle(where, __func__, bh, ext4_journal_abort_handle(where, __func__, bh, handle, err);
handle, err); ext4_abort(inode->i_sb, __func__,
"error %d when attempting revoke", err);
} }
else BUFFER_TRACE(bh, "exit");
bforget(bh);
return err; return err;
} }
......
...@@ -49,7 +49,7 @@ ...@@ -49,7 +49,7 @@
#define EXT4_DATA_TRANS_BLOCKS(sb) (EXT4_SINGLEDATA_TRANS_BLOCKS(sb) + \ #define EXT4_DATA_TRANS_BLOCKS(sb) (EXT4_SINGLEDATA_TRANS_BLOCKS(sb) + \
EXT4_XATTR_TRANS_BLOCKS - 2 + \ EXT4_XATTR_TRANS_BLOCKS - 2 + \
2*EXT4_QUOTA_TRANS_BLOCKS(sb)) EXT4_MAXQUOTAS_TRANS_BLOCKS(sb))
/* /*
* Define the number of metadata blocks we need to account to modify data. * Define the number of metadata blocks we need to account to modify data.
...@@ -57,7 +57,7 @@ ...@@ -57,7 +57,7 @@
* This include super block, inode block, quota blocks and xattr blocks * This include super block, inode block, quota blocks and xattr blocks
*/ */
#define EXT4_META_TRANS_BLOCKS(sb) (EXT4_XATTR_TRANS_BLOCKS + \ #define EXT4_META_TRANS_BLOCKS(sb) (EXT4_XATTR_TRANS_BLOCKS + \
2*EXT4_QUOTA_TRANS_BLOCKS(sb)) EXT4_MAXQUOTAS_TRANS_BLOCKS(sb))
/* Delete operations potentially hit one directory's namespace plus an /* Delete operations potentially hit one directory's namespace plus an
* entire inode, plus arbitrary amounts of bitmap/indirection data. Be * entire inode, plus arbitrary amounts of bitmap/indirection data. Be
...@@ -92,6 +92,7 @@ ...@@ -92,6 +92,7 @@
* but inode, sb and group updates are done only once */ * but inode, sb and group updates are done only once */
#define EXT4_QUOTA_INIT_BLOCKS(sb) (test_opt(sb, QUOTA) ? (DQUOT_INIT_ALLOC*\ #define EXT4_QUOTA_INIT_BLOCKS(sb) (test_opt(sb, QUOTA) ? (DQUOT_INIT_ALLOC*\
(EXT4_SINGLEDATA_TRANS_BLOCKS(sb)-3)+3+DQUOT_INIT_REWRITE) : 0) (EXT4_SINGLEDATA_TRANS_BLOCKS(sb)-3)+3+DQUOT_INIT_REWRITE) : 0)
#define EXT4_QUOTA_DEL_BLOCKS(sb) (test_opt(sb, QUOTA) ? (DQUOT_DEL_ALLOC*\ #define EXT4_QUOTA_DEL_BLOCKS(sb) (test_opt(sb, QUOTA) ? (DQUOT_DEL_ALLOC*\
(EXT4_SINGLEDATA_TRANS_BLOCKS(sb)-3)+3+DQUOT_DEL_REWRITE) : 0) (EXT4_SINGLEDATA_TRANS_BLOCKS(sb)-3)+3+DQUOT_DEL_REWRITE) : 0)
#else #else
...@@ -99,6 +100,9 @@ ...@@ -99,6 +100,9 @@
#define EXT4_QUOTA_INIT_BLOCKS(sb) 0 #define EXT4_QUOTA_INIT_BLOCKS(sb) 0
#define EXT4_QUOTA_DEL_BLOCKS(sb) 0 #define EXT4_QUOTA_DEL_BLOCKS(sb) 0
#endif #endif
#define EXT4_MAXQUOTAS_TRANS_BLOCKS(sb) (MAXQUOTAS*EXT4_QUOTA_TRANS_BLOCKS(sb))
#define EXT4_MAXQUOTAS_INIT_BLOCKS(sb) (MAXQUOTAS*EXT4_QUOTA_INIT_BLOCKS(sb))
#define EXT4_MAXQUOTAS_DEL_BLOCKS(sb) (MAXQUOTAS*EXT4_QUOTA_DEL_BLOCKS(sb))
int int
ext4_mark_iloc_dirty(handle_t *handle, ext4_mark_iloc_dirty(handle_t *handle,
...@@ -116,12 +120,8 @@ int ext4_reserve_inode_write(handle_t *handle, struct inode *inode, ...@@ -116,12 +120,8 @@ int ext4_reserve_inode_write(handle_t *handle, struct inode *inode,
int ext4_mark_inode_dirty(handle_t *handle, struct inode *inode); int ext4_mark_inode_dirty(handle_t *handle, struct inode *inode);
/* /*
* Wrapper functions with which ext4 calls into JBD. The intent here is * Wrapper functions with which ext4 calls into JBD.
* to allow these to be turned into appropriate stubs so ext4 can control
* ext2 filesystems, so ext2+ext4 systems only nee one fs. This work hasn't
* been done yet.
*/ */
void ext4_journal_abort_handle(const char *caller, const char *err_fn, void ext4_journal_abort_handle(const char *caller, const char *err_fn,
struct buffer_head *bh, handle_t *handle, int err); struct buffer_head *bh, handle_t *handle, int err);
...@@ -131,13 +131,9 @@ int __ext4_journal_get_undo_access(const char *where, handle_t *handle, ...@@ -131,13 +131,9 @@ int __ext4_journal_get_undo_access(const char *where, handle_t *handle,
int __ext4_journal_get_write_access(const char *where, handle_t *handle, int __ext4_journal_get_write_access(const char *where, handle_t *handle,
struct buffer_head *bh); struct buffer_head *bh);
/* When called with an invalid handle, this will still do a put on the BH */ int __ext4_forget(const char *where, handle_t *handle, int is_metadata,
int __ext4_journal_forget(const char *where, handle_t *handle, struct inode *inode, struct buffer_head *bh,
struct buffer_head *bh); ext4_fsblk_t blocknr);
/* When called with an invalid handle, this will still do a put on the BH */
int __ext4_journal_revoke(const char *where, handle_t *handle,
ext4_fsblk_t blocknr, struct buffer_head *bh);
int __ext4_journal_get_create_access(const char *where, int __ext4_journal_get_create_access(const char *where,
handle_t *handle, struct buffer_head *bh); handle_t *handle, struct buffer_head *bh);
...@@ -149,12 +145,11 @@ int __ext4_handle_dirty_metadata(const char *where, handle_t *handle, ...@@ -149,12 +145,11 @@ int __ext4_handle_dirty_metadata(const char *where, handle_t *handle,
__ext4_journal_get_undo_access(__func__, (handle), (bh)) __ext4_journal_get_undo_access(__func__, (handle), (bh))
#define ext4_journal_get_write_access(handle, bh) \ #define ext4_journal_get_write_access(handle, bh) \
__ext4_journal_get_write_access(__func__, (handle), (bh)) __ext4_journal_get_write_access(__func__, (handle), (bh))
#define ext4_journal_revoke(handle, blocknr, bh) \ #define ext4_forget(handle, is_metadata, inode, bh, block_nr) \
__ext4_journal_revoke(__func__, (handle), (blocknr), (bh)) __ext4_forget(__func__, (handle), (is_metadata), (inode), (bh),\
(block_nr))
#define ext4_journal_get_create_access(handle, bh) \ #define ext4_journal_get_create_access(handle, bh) \
__ext4_journal_get_create_access(__func__, (handle), (bh)) __ext4_journal_get_create_access(__func__, (handle), (bh))
#define ext4_journal_forget(handle, bh) \
__ext4_journal_forget(__func__, (handle), (bh))
#define ext4_handle_dirty_metadata(handle, inode, bh) \ #define ext4_handle_dirty_metadata(handle, inode, bh) \
__ext4_handle_dirty_metadata(__func__, (handle), (inode), (bh)) __ext4_handle_dirty_metadata(__func__, (handle), (inode), (bh))
...@@ -254,6 +249,19 @@ static inline int ext4_jbd2_file_inode(handle_t *handle, struct inode *inode) ...@@ -254,6 +249,19 @@ static inline int ext4_jbd2_file_inode(handle_t *handle, struct inode *inode)
return 0; return 0;
} }
static inline void ext4_update_inode_fsync_trans(handle_t *handle,
struct inode *inode,
int datasync)
{
struct ext4_inode_info *ei = EXT4_I(inode);
if (ext4_handle_valid(handle)) {
ei->i_sync_tid = handle->h_transaction->t_tid;
if (datasync)
ei->i_datasync_tid = handle->h_transaction->t_tid;
}
}
/* super.c */ /* super.c */
int ext4_force_commit(struct super_block *sb); int ext4_force_commit(struct super_block *sb);
......
...@@ -1007,7 +1007,8 @@ static int ext4_ext_split(handle_t *handle, struct inode *inode, ...@@ -1007,7 +1007,8 @@ static int ext4_ext_split(handle_t *handle, struct inode *inode,
for (i = 0; i < depth; i++) { for (i = 0; i < depth; i++) {
if (!ablocks[i]) if (!ablocks[i])
continue; continue;
ext4_free_blocks(handle, inode, ablocks[i], 1, 1); ext4_free_blocks(handle, inode, 0, ablocks[i], 1,
EXT4_FREE_BLOCKS_METADATA);
} }
} }
kfree(ablocks); kfree(ablocks);
...@@ -1761,7 +1762,9 @@ int ext4_ext_walk_space(struct inode *inode, ext4_lblk_t block, ...@@ -1761,7 +1762,9 @@ int ext4_ext_walk_space(struct inode *inode, ext4_lblk_t block,
while (block < last && block != EXT_MAX_BLOCK) { while (block < last && block != EXT_MAX_BLOCK) {
num = last - block; num = last - block;
/* find extent for this block */ /* find extent for this block */
down_read(&EXT4_I(inode)->i_data_sem);
path = ext4_ext_find_extent(inode, block, path); path = ext4_ext_find_extent(inode, block, path);
up_read(&EXT4_I(inode)->i_data_sem);
if (IS_ERR(path)) { if (IS_ERR(path)) {
err = PTR_ERR(path); err = PTR_ERR(path);
path = NULL; path = NULL;
...@@ -1957,7 +1960,6 @@ ext4_ext_in_cache(struct inode *inode, ext4_lblk_t block, ...@@ -1957,7 +1960,6 @@ ext4_ext_in_cache(struct inode *inode, ext4_lblk_t block,
static int ext4_ext_rm_idx(handle_t *handle, struct inode *inode, static int ext4_ext_rm_idx(handle_t *handle, struct inode *inode,
struct ext4_ext_path *path) struct ext4_ext_path *path)
{ {
struct buffer_head *bh;
int err; int err;
ext4_fsblk_t leaf; ext4_fsblk_t leaf;
...@@ -1973,9 +1975,8 @@ static int ext4_ext_rm_idx(handle_t *handle, struct inode *inode, ...@@ -1973,9 +1975,8 @@ static int ext4_ext_rm_idx(handle_t *handle, struct inode *inode,
if (err) if (err)
return err; return err;
ext_debug("index is empty, remove it, free block %llu\n", leaf); ext_debug("index is empty, remove it, free block %llu\n", leaf);
bh = sb_find_get_block(inode->i_sb, leaf); ext4_free_blocks(handle, inode, 0, leaf, 1,
ext4_forget(handle, 1, inode, bh, leaf); EXT4_FREE_BLOCKS_METADATA | EXT4_FREE_BLOCKS_FORGET);
ext4_free_blocks(handle, inode, leaf, 1, 1);
return err; return err;
} }
...@@ -2042,12 +2043,11 @@ static int ext4_remove_blocks(handle_t *handle, struct inode *inode, ...@@ -2042,12 +2043,11 @@ static int ext4_remove_blocks(handle_t *handle, struct inode *inode,
struct ext4_extent *ex, struct ext4_extent *ex,
ext4_lblk_t from, ext4_lblk_t to) ext4_lblk_t from, ext4_lblk_t to)
{ {
struct buffer_head *bh;
unsigned short ee_len = ext4_ext_get_actual_len(ex); unsigned short ee_len = ext4_ext_get_actual_len(ex);
int i, metadata = 0; int flags = EXT4_FREE_BLOCKS_FORGET;
if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))
metadata = 1; flags |= EXT4_FREE_BLOCKS_METADATA;
#ifdef EXTENTS_STATS #ifdef EXTENTS_STATS
{ {
struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
...@@ -2072,11 +2072,7 @@ static int ext4_remove_blocks(handle_t *handle, struct inode *inode, ...@@ -2072,11 +2072,7 @@ static int ext4_remove_blocks(handle_t *handle, struct inode *inode,
num = le32_to_cpu(ex->ee_block) + ee_len - from; num = le32_to_cpu(ex->ee_block) + ee_len - from;
start = ext_pblock(ex) + ee_len - num; start = ext_pblock(ex) + ee_len - num;
ext_debug("free last %u blocks starting %llu\n", num, start); ext_debug("free last %u blocks starting %llu\n", num, start);
for (i = 0; i < num; i++) { ext4_free_blocks(handle, inode, 0, start, num, flags);
bh = sb_find_get_block(inode->i_sb, start + i);
ext4_forget(handle, 0, inode, bh, start + i);
}
ext4_free_blocks(handle, inode, start, num, metadata);
} else if (from == le32_to_cpu(ex->ee_block) } else if (from == le32_to_cpu(ex->ee_block)
&& to <= le32_to_cpu(ex->ee_block) + ee_len - 1) { && to <= le32_to_cpu(ex->ee_block) + ee_len - 1) {
printk(KERN_INFO "strange request: removal %u-%u from %u:%u\n", printk(KERN_INFO "strange request: removal %u-%u from %u:%u\n",
...@@ -2167,7 +2163,7 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode, ...@@ -2167,7 +2163,7 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode,
correct_index = 1; correct_index = 1;
credits += (ext_depth(inode)) + 1; credits += (ext_depth(inode)) + 1;
} }
credits += 2 * EXT4_QUOTA_TRANS_BLOCKS(inode->i_sb); credits += EXT4_MAXQUOTAS_TRANS_BLOCKS(inode->i_sb);
err = ext4_ext_truncate_extend_restart(handle, inode, credits); err = ext4_ext_truncate_extend_restart(handle, inode, credits);
if (err) if (err)
...@@ -3064,6 +3060,8 @@ ext4_ext_handle_uninitialized_extents(handle_t *handle, struct inode *inode, ...@@ -3064,6 +3060,8 @@ ext4_ext_handle_uninitialized_extents(handle_t *handle, struct inode *inode,
if (flags == EXT4_GET_BLOCKS_DIO_CONVERT_EXT) { if (flags == EXT4_GET_BLOCKS_DIO_CONVERT_EXT) {
ret = ext4_convert_unwritten_extents_dio(handle, inode, ret = ext4_convert_unwritten_extents_dio(handle, inode,
path); path);
if (ret >= 0)
ext4_update_inode_fsync_trans(handle, inode, 1);
goto out2; goto out2;
} }
/* buffered IO case */ /* buffered IO case */
...@@ -3091,6 +3089,8 @@ ext4_ext_handle_uninitialized_extents(handle_t *handle, struct inode *inode, ...@@ -3091,6 +3089,8 @@ ext4_ext_handle_uninitialized_extents(handle_t *handle, struct inode *inode,
ret = ext4_ext_convert_to_initialized(handle, inode, ret = ext4_ext_convert_to_initialized(handle, inode,
path, iblock, path, iblock,
max_blocks); max_blocks);
if (ret >= 0)
ext4_update_inode_fsync_trans(handle, inode, 1);
out: out:
if (ret <= 0) { if (ret <= 0) {
err = ret; err = ret;
...@@ -3319,7 +3319,7 @@ int ext4_ext_get_blocks(handle_t *handle, struct inode *inode, ...@@ -3319,7 +3319,7 @@ int ext4_ext_get_blocks(handle_t *handle, struct inode *inode,
/* not a good idea to call discard here directly, /* not a good idea to call discard here directly,
* but otherwise we'd need to call it every free() */ * but otherwise we'd need to call it every free() */
ext4_discard_preallocations(inode); ext4_discard_preallocations(inode);
ext4_free_blocks(handle, inode, ext_pblock(&newex), ext4_free_blocks(handle, inode, 0, ext_pblock(&newex),
ext4_ext_get_actual_len(&newex), 0); ext4_ext_get_actual_len(&newex), 0);
goto out2; goto out2;
} }
...@@ -3329,10 +3329,16 @@ int ext4_ext_get_blocks(handle_t *handle, struct inode *inode, ...@@ -3329,10 +3329,16 @@ int ext4_ext_get_blocks(handle_t *handle, struct inode *inode,
allocated = ext4_ext_get_actual_len(&newex); allocated = ext4_ext_get_actual_len(&newex);
set_buffer_new(bh_result); set_buffer_new(bh_result);
/* Cache only when it is _not_ an uninitialized extent */ /*
if ((flags & EXT4_GET_BLOCKS_UNINIT_EXT) == 0) * Cache the extent and update transaction to commit on fdatasync only
* when it is _not_ an uninitialized extent.
*/
if ((flags & EXT4_GET_BLOCKS_UNINIT_EXT) == 0) {
ext4_ext_put_in_cache(inode, iblock, allocated, newblock, ext4_ext_put_in_cache(inode, iblock, allocated, newblock,
EXT4_EXT_CACHE_EXTENT); EXT4_EXT_CACHE_EXTENT);
ext4_update_inode_fsync_trans(handle, inode, 1);
} else
ext4_update_inode_fsync_trans(handle, inode, 0);
out: out:
if (allocated > max_blocks) if (allocated > max_blocks)
allocated = max_blocks; allocated = max_blocks;
...@@ -3720,10 +3726,8 @@ int ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, ...@@ -3720,10 +3726,8 @@ int ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
* Walk the extent tree gathering extent information. * Walk the extent tree gathering extent information.
* ext4_ext_fiemap_cb will push extents back to user. * ext4_ext_fiemap_cb will push extents back to user.
*/ */
down_read(&EXT4_I(inode)->i_data_sem);
error = ext4_ext_walk_space(inode, start_blk, len_blks, error = ext4_ext_walk_space(inode, start_blk, len_blks,
ext4_ext_fiemap_cb, fieinfo); ext4_ext_fiemap_cb, fieinfo);
up_read(&EXT4_I(inode)->i_data_sem);
} }
return error; return error;
......
...@@ -51,25 +51,30 @@ ...@@ -51,25 +51,30 @@
int ext4_sync_file(struct file *file, struct dentry *dentry, int datasync) int ext4_sync_file(struct file *file, struct dentry *dentry, int datasync)
{ {
struct inode *inode = dentry->d_inode; struct inode *inode = dentry->d_inode;
struct ext4_inode_info *ei = EXT4_I(inode);
journal_t *journal = EXT4_SB(inode->i_sb)->s_journal; journal_t *journal = EXT4_SB(inode->i_sb)->s_journal;
int err, ret = 0; int ret;
tid_t commit_tid;
J_ASSERT(ext4_journal_current_handle() == NULL); J_ASSERT(ext4_journal_current_handle() == NULL);
trace_ext4_sync_file(file, dentry, datasync); trace_ext4_sync_file(file, dentry, datasync);
if (inode->i_sb->s_flags & MS_RDONLY)
return 0;
ret = flush_aio_dio_completed_IO(inode); ret = flush_aio_dio_completed_IO(inode);
if (ret < 0) if (ret < 0)
goto out; return ret;
if (!journal)
return simple_fsync(file, dentry, datasync);
/* /*
* data=writeback: * data=writeback,ordered:
* The caller's filemap_fdatawrite()/wait will sync the data. * The caller's filemap_fdatawrite()/wait will sync the data.
* sync_inode() will sync the metadata * Metadata is in the journal, we wait for proper transaction to
* * commit here.
* data=ordered:
* The caller's filemap_fdatawrite() will write the data and
* sync_inode() will write the inode if it is dirty. Then the caller's
* filemap_fdatawait() will wait on the pages.
* *
* data=journal: * data=journal:
* filemap_fdatawrite won't do anything (the buffers are clean). * filemap_fdatawrite won't do anything (the buffers are clean).
...@@ -79,32 +84,13 @@ int ext4_sync_file(struct file *file, struct dentry *dentry, int datasync) ...@@ -79,32 +84,13 @@ int ext4_sync_file(struct file *file, struct dentry *dentry, int datasync)
* (they were dirtied by commit). But that's OK - the blocks are * (they were dirtied by commit). But that's OK - the blocks are
* safe in-journal, which is all fsync() needs to ensure. * safe in-journal, which is all fsync() needs to ensure.
*/ */
if (ext4_should_journal_data(inode)) { if (ext4_should_journal_data(inode))
ret = ext4_force_commit(inode->i_sb); return ext4_force_commit(inode->i_sb);
goto out;
}
if (!journal) commit_tid = datasync ? ei->i_datasync_tid : ei->i_sync_tid;
ret = sync_mapping_buffers(inode->i_mapping); if (jbd2_log_start_commit(journal, commit_tid))
jbd2_log_wait_commit(journal, commit_tid);
if (datasync && !(inode->i_state & I_DIRTY_DATASYNC)) else if (journal->j_flags & JBD2_BARRIER)
goto out;
/*
* The VFS has written the file data. If the inode is unaltered
* then we need not start a commit.
*/
if (inode->i_state & (I_DIRTY_SYNC|I_DIRTY_DATASYNC)) {
struct writeback_control wbc = {
.sync_mode = WB_SYNC_ALL,
.nr_to_write = 0, /* sys_fsync did this */
};
err = sync_inode(inode, &wbc);
if (ret == 0)
ret = err;
}
out:
if (journal && (journal->j_flags & JBD2_BARRIER))
blkdev_issue_flush(inode->i_sb->s_bdev, NULL); blkdev_issue_flush(inode->i_sb->s_bdev, NULL);
return ret; return ret;
} }
This diff is collapsed.
...@@ -221,31 +221,38 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) ...@@ -221,31 +221,38 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
struct file *donor_filp; struct file *donor_filp;
int err; int err;
if (!(filp->f_mode & FMODE_READ) ||
!(filp->f_mode & FMODE_WRITE))
return -EBADF;
if (copy_from_user(&me, if (copy_from_user(&me,
(struct move_extent __user *)arg, sizeof(me))) (struct move_extent __user *)arg, sizeof(me)))
return -EFAULT; return -EFAULT;
me.moved_len = 0;
donor_filp = fget(me.donor_fd); donor_filp = fget(me.donor_fd);
if (!donor_filp) if (!donor_filp)
return -EBADF; return -EBADF;
if (!capable(CAP_DAC_OVERRIDE)) { if (!(donor_filp->f_mode & FMODE_WRITE)) {
if ((current->real_cred->fsuid != inode->i_uid) || err = -EBADF;
!(inode->i_mode & S_IRUSR) || goto mext_out;
!(donor_filp->f_dentry->d_inode->i_mode &
S_IRUSR)) {
fput(donor_filp);
return -EACCES;
}
} }
err = mnt_want_write(filp->f_path.mnt);
if (err)
goto mext_out;
err = ext4_move_extents(filp, donor_filp, me.orig_start, err = ext4_move_extents(filp, donor_filp, me.orig_start,
me.donor_start, me.len, &me.moved_len); me.donor_start, me.len, &me.moved_len);
fput(donor_filp); mnt_drop_write(filp->f_path.mnt);
if (me.moved_len > 0)
file_remove_suid(donor_filp);
if (copy_to_user((struct move_extent *)arg, &me, sizeof(me))) if (copy_to_user((struct move_extent *)arg, &me, sizeof(me)))
return -EFAULT; err = -EFAULT;
mext_out:
fput(donor_filp);
return err; return err;
} }
......
...@@ -2529,7 +2529,6 @@ static void release_blocks_on_commit(journal_t *journal, transaction_t *txn) ...@@ -2529,7 +2529,6 @@ static void release_blocks_on_commit(journal_t *journal, transaction_t *txn)
struct ext4_group_info *db; struct ext4_group_info *db;
int err, count = 0, count2 = 0; int err, count = 0, count2 = 0;
struct ext4_free_data *entry; struct ext4_free_data *entry;
ext4_fsblk_t discard_block;
struct list_head *l, *ltmp; struct list_head *l, *ltmp;
list_for_each_safe(l, ltmp, &txn->t_private_list) { list_for_each_safe(l, ltmp, &txn->t_private_list) {
...@@ -2559,13 +2558,19 @@ static void release_blocks_on_commit(journal_t *journal, transaction_t *txn) ...@@ -2559,13 +2558,19 @@ static void release_blocks_on_commit(journal_t *journal, transaction_t *txn)
page_cache_release(e4b.bd_bitmap_page); page_cache_release(e4b.bd_bitmap_page);
} }
ext4_unlock_group(sb, entry->group); ext4_unlock_group(sb, entry->group);
discard_block = (ext4_fsblk_t) entry->group * EXT4_BLOCKS_PER_GROUP(sb) if (test_opt(sb, DISCARD)) {
ext4_fsblk_t discard_block;
struct ext4_super_block *es = EXT4_SB(sb)->s_es;
discard_block = (ext4_fsblk_t)entry->group *
EXT4_BLOCKS_PER_GROUP(sb)
+ entry->start_blk + entry->start_blk
+ le32_to_cpu(EXT4_SB(sb)->s_es->s_first_data_block); + le32_to_cpu(es->s_first_data_block);
trace_ext4_discard_blocks(sb, (unsigned long long)discard_block, trace_ext4_discard_blocks(sb,
(unsigned long long)discard_block,
entry->count); entry->count);
sb_issue_discard(sb, discard_block, entry->count); sb_issue_discard(sb, discard_block, entry->count);
}
kmem_cache_free(ext4_free_ext_cachep, entry); kmem_cache_free(ext4_free_ext_cachep, entry);
ext4_mb_release_desc(&e4b); ext4_mb_release_desc(&e4b);
} }
...@@ -3005,6 +3010,24 @@ static void ext4_mb_collect_stats(struct ext4_allocation_context *ac) ...@@ -3005,6 +3010,24 @@ static void ext4_mb_collect_stats(struct ext4_allocation_context *ac)
trace_ext4_mballoc_prealloc(ac); trace_ext4_mballoc_prealloc(ac);
} }
/*
* Called on failure; free up any blocks from the inode PA for this
* context. We don't need this for MB_GROUP_PA because we only change
* pa_free in ext4_mb_release_context(), but on failure, we've already
* zeroed out ac->ac_b_ex.fe_len, so group_pa->pa_free is not changed.
*/
static void ext4_discard_allocated_blocks(struct ext4_allocation_context *ac)
{
struct ext4_prealloc_space *pa = ac->ac_pa;
int len;
if (pa && pa->pa_type == MB_INODE_PA) {
len = ac->ac_b_ex.fe_len;
pa->pa_free += len;
}
}
/* /*
* use blocks preallocated to inode * use blocks preallocated to inode
*/ */
...@@ -4290,6 +4313,7 @@ ext4_fsblk_t ext4_mb_new_blocks(handle_t *handle, ...@@ -4290,6 +4313,7 @@ ext4_fsblk_t ext4_mb_new_blocks(handle_t *handle,
ac->ac_status = AC_STATUS_CONTINUE; ac->ac_status = AC_STATUS_CONTINUE;
goto repeat; goto repeat;
} else if (*errp) { } else if (*errp) {
ext4_discard_allocated_blocks(ac);
ac->ac_b_ex.fe_len = 0; ac->ac_b_ex.fe_len = 0;
ar->len = 0; ar->len = 0;
ext4_mb_show_ac(ac); ext4_mb_show_ac(ac);
...@@ -4422,18 +4446,24 @@ ext4_mb_free_metadata(handle_t *handle, struct ext4_buddy *e4b, ...@@ -4422,18 +4446,24 @@ ext4_mb_free_metadata(handle_t *handle, struct ext4_buddy *e4b,
return 0; return 0;
} }
/* /**
* Main entry point into mballoc to free blocks * ext4_free_blocks() -- Free given blocks and update quota
* @handle: handle for this transaction
* @inode: inode
* @block: start physical block to free
* @count: number of blocks to count
* @metadata: Are these metadata blocks
*/ */
void ext4_mb_free_blocks(handle_t *handle, struct inode *inode, void ext4_free_blocks(handle_t *handle, struct inode *inode,
ext4_fsblk_t block, unsigned long count, struct buffer_head *bh, ext4_fsblk_t block,
int metadata, unsigned long *freed) unsigned long count, int flags)
{ {
struct buffer_head *bitmap_bh = NULL; struct buffer_head *bitmap_bh = NULL;
struct super_block *sb = inode->i_sb; struct super_block *sb = inode->i_sb;
struct ext4_allocation_context *ac = NULL; struct ext4_allocation_context *ac = NULL;
struct ext4_group_desc *gdp; struct ext4_group_desc *gdp;
struct ext4_super_block *es; struct ext4_super_block *es;
unsigned long freed = 0;
unsigned int overflow; unsigned int overflow;
ext4_grpblk_t bit; ext4_grpblk_t bit;
struct buffer_head *gd_bh; struct buffer_head *gd_bh;
...@@ -4443,13 +4473,16 @@ void ext4_mb_free_blocks(handle_t *handle, struct inode *inode, ...@@ -4443,13 +4473,16 @@ void ext4_mb_free_blocks(handle_t *handle, struct inode *inode,
int err = 0; int err = 0;
int ret; int ret;
*freed = 0; if (bh) {
if (block)
BUG_ON(block != bh->b_blocknr);
else
block = bh->b_blocknr;
}
sbi = EXT4_SB(sb); sbi = EXT4_SB(sb);
es = EXT4_SB(sb)->s_es; es = EXT4_SB(sb)->s_es;
if (block < le32_to_cpu(es->s_first_data_block) || if (!ext4_data_block_valid(sbi, block, count)) {
block + count < block ||
block + count > ext4_blocks_count(es)) {
ext4_error(sb, __func__, ext4_error(sb, __func__,
"Freeing blocks not in datazone - " "Freeing blocks not in datazone - "
"block = %llu, count = %lu", block, count); "block = %llu, count = %lu", block, count);
...@@ -4457,7 +4490,32 @@ void ext4_mb_free_blocks(handle_t *handle, struct inode *inode, ...@@ -4457,7 +4490,32 @@ void ext4_mb_free_blocks(handle_t *handle, struct inode *inode,
} }
ext4_debug("freeing block %llu\n", block); ext4_debug("freeing block %llu\n", block);
trace_ext4_free_blocks(inode, block, count, metadata); trace_ext4_free_blocks(inode, block, count, flags);
if (flags & EXT4_FREE_BLOCKS_FORGET) {
struct buffer_head *tbh = bh;
int i;
BUG_ON(bh && (count > 1));
for (i = 0; i < count; i++) {
if (!bh)
tbh = sb_find_get_block(inode->i_sb,
block + i);
ext4_forget(handle, flags & EXT4_FREE_BLOCKS_METADATA,
inode, tbh, block + i);
}
}
/*
* We need to make sure we don't reuse the freed block until
* after the transaction is committed, which we can do by
* treating the block as metadata, below. We make an
* exception if the inode is to be written in writeback mode
* since writeback mode has weak data consistency guarantees.
*/
if (!ext4_should_writeback_data(inode))
flags |= EXT4_FREE_BLOCKS_METADATA;
ac = kmem_cache_alloc(ext4_ac_cachep, GFP_NOFS); ac = kmem_cache_alloc(ext4_ac_cachep, GFP_NOFS);
if (ac) { if (ac) {
...@@ -4533,7 +4591,8 @@ void ext4_mb_free_blocks(handle_t *handle, struct inode *inode, ...@@ -4533,7 +4591,8 @@ void ext4_mb_free_blocks(handle_t *handle, struct inode *inode,
err = ext4_mb_load_buddy(sb, block_group, &e4b); err = ext4_mb_load_buddy(sb, block_group, &e4b);
if (err) if (err)
goto error_return; goto error_return;
if (metadata && ext4_handle_valid(handle)) {
if ((flags & EXT4_FREE_BLOCKS_METADATA) && ext4_handle_valid(handle)) {
struct ext4_free_data *new_entry; struct ext4_free_data *new_entry;
/* /*
* blocks being freed are metadata. these blocks shouldn't * blocks being freed are metadata. these blocks shouldn't
...@@ -4572,7 +4631,7 @@ void ext4_mb_free_blocks(handle_t *handle, struct inode *inode, ...@@ -4572,7 +4631,7 @@ void ext4_mb_free_blocks(handle_t *handle, struct inode *inode,
ext4_mb_release_desc(&e4b); ext4_mb_release_desc(&e4b);
*freed += count; freed += count;
/* We dirtied the bitmap block */ /* We dirtied the bitmap block */
BUFFER_TRACE(bitmap_bh, "dirtied bitmap block"); BUFFER_TRACE(bitmap_bh, "dirtied bitmap block");
...@@ -4592,6 +4651,8 @@ void ext4_mb_free_blocks(handle_t *handle, struct inode *inode, ...@@ -4592,6 +4651,8 @@ void ext4_mb_free_blocks(handle_t *handle, struct inode *inode,
} }
sb->s_dirt = 1; sb->s_dirt = 1;
error_return: error_return:
if (freed)
vfs_dq_free_block(inode, freed);
brelse(bitmap_bh); brelse(bitmap_bh);
ext4_std_error(sb, err); ext4_std_error(sb, err);
if (ac) if (ac)
......
...@@ -238,7 +238,7 @@ static int extend_credit_for_blkdel(handle_t *handle, struct inode *inode) ...@@ -238,7 +238,7 @@ static int extend_credit_for_blkdel(handle_t *handle, struct inode *inode)
* So allocate a credit of 3. We may update * So allocate a credit of 3. We may update
* quota (user and group). * quota (user and group).
*/ */
needed = 3 + 2*EXT4_QUOTA_TRANS_BLOCKS(inode->i_sb); needed = 3 + EXT4_MAXQUOTAS_TRANS_BLOCKS(inode->i_sb);
if (ext4_journal_extend(handle, needed) != 0) if (ext4_journal_extend(handle, needed) != 0)
retval = ext4_journal_restart(handle, needed); retval = ext4_journal_restart(handle, needed);
...@@ -262,13 +262,17 @@ static int free_dind_blocks(handle_t *handle, ...@@ -262,13 +262,17 @@ static int free_dind_blocks(handle_t *handle,
for (i = 0; i < max_entries; i++) { for (i = 0; i < max_entries; i++) {
if (tmp_idata[i]) { if (tmp_idata[i]) {
extend_credit_for_blkdel(handle, inode); extend_credit_for_blkdel(handle, inode);
ext4_free_blocks(handle, inode, ext4_free_blocks(handle, inode, 0,
le32_to_cpu(tmp_idata[i]), 1, 1); le32_to_cpu(tmp_idata[i]), 1,
EXT4_FREE_BLOCKS_METADATA |
EXT4_FREE_BLOCKS_FORGET);
} }
} }
put_bh(bh); put_bh(bh);
extend_credit_for_blkdel(handle, inode); extend_credit_for_blkdel(handle, inode);
ext4_free_blocks(handle, inode, le32_to_cpu(i_data), 1, 1); ext4_free_blocks(handle, inode, 0, le32_to_cpu(i_data), 1,
EXT4_FREE_BLOCKS_METADATA |
EXT4_FREE_BLOCKS_FORGET);
return 0; return 0;
} }
...@@ -297,7 +301,9 @@ static int free_tind_blocks(handle_t *handle, ...@@ -297,7 +301,9 @@ static int free_tind_blocks(handle_t *handle,
} }
put_bh(bh); put_bh(bh);
extend_credit_for_blkdel(handle, inode); extend_credit_for_blkdel(handle, inode);
ext4_free_blocks(handle, inode, le32_to_cpu(i_data), 1, 1); ext4_free_blocks(handle, inode, 0, le32_to_cpu(i_data), 1,
EXT4_FREE_BLOCKS_METADATA |
EXT4_FREE_BLOCKS_FORGET);
return 0; return 0;
} }
...@@ -308,8 +314,10 @@ static int free_ind_block(handle_t *handle, struct inode *inode, __le32 *i_data) ...@@ -308,8 +314,10 @@ static int free_ind_block(handle_t *handle, struct inode *inode, __le32 *i_data)
/* ei->i_data[EXT4_IND_BLOCK] */ /* ei->i_data[EXT4_IND_BLOCK] */
if (i_data[0]) { if (i_data[0]) {
extend_credit_for_blkdel(handle, inode); extend_credit_for_blkdel(handle, inode);
ext4_free_blocks(handle, inode, ext4_free_blocks(handle, inode, 0,
le32_to_cpu(i_data[0]), 1, 1); le32_to_cpu(i_data[0]), 1,
EXT4_FREE_BLOCKS_METADATA |
EXT4_FREE_BLOCKS_FORGET);
} }
/* ei->i_data[EXT4_DIND_BLOCK] */ /* ei->i_data[EXT4_DIND_BLOCK] */
...@@ -419,7 +427,8 @@ static int free_ext_idx(handle_t *handle, struct inode *inode, ...@@ -419,7 +427,8 @@ static int free_ext_idx(handle_t *handle, struct inode *inode,
} }
put_bh(bh); put_bh(bh);
extend_credit_for_blkdel(handle, inode); extend_credit_for_blkdel(handle, inode);
ext4_free_blocks(handle, inode, block, 1, 1); ext4_free_blocks(handle, inode, 0, block, 1,
EXT4_FREE_BLOCKS_METADATA | EXT4_FREE_BLOCKS_FORGET);
return retval; return retval;
} }
...@@ -477,7 +486,7 @@ int ext4_ext_migrate(struct inode *inode) ...@@ -477,7 +486,7 @@ int ext4_ext_migrate(struct inode *inode)
handle = ext4_journal_start(inode, handle = ext4_journal_start(inode,
EXT4_DATA_TRANS_BLOCKS(inode->i_sb) + EXT4_DATA_TRANS_BLOCKS(inode->i_sb) +
EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3 + EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3 +
2 * EXT4_QUOTA_INIT_BLOCKS(inode->i_sb) EXT4_MAXQUOTAS_INIT_BLOCKS(inode->i_sb)
+ 1); + 1);
if (IS_ERR(handle)) { if (IS_ERR(handle)) {
retval = PTR_ERR(handle); retval = PTR_ERR(handle);
......
This diff is collapsed.
...@@ -1292,9 +1292,6 @@ static struct ext4_dir_entry_2 *do_split(handle_t *handle, struct inode *dir, ...@@ -1292,9 +1292,6 @@ static struct ext4_dir_entry_2 *do_split(handle_t *handle, struct inode *dir,
* add_dirent_to_buf will attempt search the directory block for * add_dirent_to_buf will attempt search the directory block for
* space. It will return -ENOSPC if no space is available, and -EIO * space. It will return -ENOSPC if no space is available, and -EIO
* and -EEXIST if directory entry already exists. * and -EEXIST if directory entry already exists.
*
* NOTE! bh is NOT released in the case where ENOSPC is returned. In
* all other cases bh is released.
*/ */
static int add_dirent_to_buf(handle_t *handle, struct dentry *dentry, static int add_dirent_to_buf(handle_t *handle, struct dentry *dentry,
struct inode *inode, struct ext4_dir_entry_2 *de, struct inode *inode, struct ext4_dir_entry_2 *de,
...@@ -1315,14 +1312,10 @@ static int add_dirent_to_buf(handle_t *handle, struct dentry *dentry, ...@@ -1315,14 +1312,10 @@ static int add_dirent_to_buf(handle_t *handle, struct dentry *dentry,
top = bh->b_data + blocksize - reclen; top = bh->b_data + blocksize - reclen;
while ((char *) de <= top) { while ((char *) de <= top) {
if (!ext4_check_dir_entry("ext4_add_entry", dir, de, if (!ext4_check_dir_entry("ext4_add_entry", dir, de,
bh, offset)) { bh, offset))
brelse(bh);
return -EIO; return -EIO;
} if (ext4_match(namelen, name, de))
if (ext4_match(namelen, name, de)) {
brelse(bh);
return -EEXIST; return -EEXIST;
}
nlen = EXT4_DIR_REC_LEN(de->name_len); nlen = EXT4_DIR_REC_LEN(de->name_len);
rlen = ext4_rec_len_from_disk(de->rec_len, blocksize); rlen = ext4_rec_len_from_disk(de->rec_len, blocksize);
if ((de->inode? rlen - nlen: rlen) >= reclen) if ((de->inode? rlen - nlen: rlen) >= reclen)
...@@ -1337,7 +1330,6 @@ static int add_dirent_to_buf(handle_t *handle, struct dentry *dentry, ...@@ -1337,7 +1330,6 @@ static int add_dirent_to_buf(handle_t *handle, struct dentry *dentry,
err = ext4_journal_get_write_access(handle, bh); err = ext4_journal_get_write_access(handle, bh);
if (err) { if (err) {
ext4_std_error(dir->i_sb, err); ext4_std_error(dir->i_sb, err);
brelse(bh);
return err; return err;
} }
...@@ -1377,7 +1369,6 @@ static int add_dirent_to_buf(handle_t *handle, struct dentry *dentry, ...@@ -1377,7 +1369,6 @@ static int add_dirent_to_buf(handle_t *handle, struct dentry *dentry,
err = ext4_handle_dirty_metadata(handle, dir, bh); err = ext4_handle_dirty_metadata(handle, dir, bh);
if (err) if (err)
ext4_std_error(dir->i_sb, err); ext4_std_error(dir->i_sb, err);
brelse(bh);
return 0; return 0;
} }
...@@ -1471,7 +1462,9 @@ static int make_indexed_dir(handle_t *handle, struct dentry *dentry, ...@@ -1471,7 +1462,9 @@ static int make_indexed_dir(handle_t *handle, struct dentry *dentry,
if (!(de)) if (!(de))
return retval; return retval;
return add_dirent_to_buf(handle, dentry, inode, de, bh); retval = add_dirent_to_buf(handle, dentry, inode, de, bh);
brelse(bh);
return retval;
} }
/* /*
...@@ -1514,8 +1507,10 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry, ...@@ -1514,8 +1507,10 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry,
if(!bh) if(!bh)
return retval; return retval;
retval = add_dirent_to_buf(handle, dentry, inode, NULL, bh); retval = add_dirent_to_buf(handle, dentry, inode, NULL, bh);
if (retval != -ENOSPC) if (retval != -ENOSPC) {
brelse(bh);
return retval; return retval;
}
if (blocks == 1 && !dx_fallback && if (blocks == 1 && !dx_fallback &&
EXT4_HAS_COMPAT_FEATURE(sb, EXT4_FEATURE_COMPAT_DIR_INDEX)) EXT4_HAS_COMPAT_FEATURE(sb, EXT4_FEATURE_COMPAT_DIR_INDEX))
...@@ -1528,7 +1523,9 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry, ...@@ -1528,7 +1523,9 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry,
de = (struct ext4_dir_entry_2 *) bh->b_data; de = (struct ext4_dir_entry_2 *) bh->b_data;
de->inode = 0; de->inode = 0;
de->rec_len = ext4_rec_len_to_disk(blocksize, blocksize); de->rec_len = ext4_rec_len_to_disk(blocksize, blocksize);
return add_dirent_to_buf(handle, dentry, inode, de, bh); retval = add_dirent_to_buf(handle, dentry, inode, de, bh);
brelse(bh);
return retval;
} }
/* /*
...@@ -1561,10 +1558,8 @@ static int ext4_dx_add_entry(handle_t *handle, struct dentry *dentry, ...@@ -1561,10 +1558,8 @@ static int ext4_dx_add_entry(handle_t *handle, struct dentry *dentry,
goto journal_error; goto journal_error;
err = add_dirent_to_buf(handle, dentry, inode, NULL, bh); err = add_dirent_to_buf(handle, dentry, inode, NULL, bh);
if (err != -ENOSPC) { if (err != -ENOSPC)
bh = NULL;
goto cleanup; goto cleanup;
}
/* Block full, should compress but for now just split */ /* Block full, should compress but for now just split */
dxtrace(printk(KERN_DEBUG "using %u of %u node entries\n", dxtrace(printk(KERN_DEBUG "using %u of %u node entries\n",
...@@ -1657,7 +1652,6 @@ static int ext4_dx_add_entry(handle_t *handle, struct dentry *dentry, ...@@ -1657,7 +1652,6 @@ static int ext4_dx_add_entry(handle_t *handle, struct dentry *dentry,
if (!de) if (!de)
goto cleanup; goto cleanup;
err = add_dirent_to_buf(handle, dentry, inode, de, bh); err = add_dirent_to_buf(handle, dentry, inode, de, bh);
bh = NULL;
goto cleanup; goto cleanup;
journal_error: journal_error:
...@@ -1775,7 +1769,7 @@ static int ext4_create(struct inode *dir, struct dentry *dentry, int mode, ...@@ -1775,7 +1769,7 @@ static int ext4_create(struct inode *dir, struct dentry *dentry, int mode,
retry: retry:
handle = ext4_journal_start(dir, EXT4_DATA_TRANS_BLOCKS(dir->i_sb) + handle = ext4_journal_start(dir, EXT4_DATA_TRANS_BLOCKS(dir->i_sb) +
EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3 + EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3 +
2*EXT4_QUOTA_INIT_BLOCKS(dir->i_sb)); EXT4_MAXQUOTAS_INIT_BLOCKS(dir->i_sb));
if (IS_ERR(handle)) if (IS_ERR(handle))
return PTR_ERR(handle); return PTR_ERR(handle);
...@@ -1809,7 +1803,7 @@ static int ext4_mknod(struct inode *dir, struct dentry *dentry, ...@@ -1809,7 +1803,7 @@ static int ext4_mknod(struct inode *dir, struct dentry *dentry,
retry: retry:
handle = ext4_journal_start(dir, EXT4_DATA_TRANS_BLOCKS(dir->i_sb) + handle = ext4_journal_start(dir, EXT4_DATA_TRANS_BLOCKS(dir->i_sb) +
EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3 + EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3 +
2*EXT4_QUOTA_INIT_BLOCKS(dir->i_sb)); EXT4_MAXQUOTAS_INIT_BLOCKS(dir->i_sb));
if (IS_ERR(handle)) if (IS_ERR(handle))
return PTR_ERR(handle); return PTR_ERR(handle);
...@@ -1846,7 +1840,7 @@ static int ext4_mkdir(struct inode *dir, struct dentry *dentry, int mode) ...@@ -1846,7 +1840,7 @@ static int ext4_mkdir(struct inode *dir, struct dentry *dentry, int mode)
retry: retry:
handle = ext4_journal_start(dir, EXT4_DATA_TRANS_BLOCKS(dir->i_sb) + handle = ext4_journal_start(dir, EXT4_DATA_TRANS_BLOCKS(dir->i_sb) +
EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3 + EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3 +
2*EXT4_QUOTA_INIT_BLOCKS(dir->i_sb)); EXT4_MAXQUOTAS_INIT_BLOCKS(dir->i_sb));
if (IS_ERR(handle)) if (IS_ERR(handle))
return PTR_ERR(handle); return PTR_ERR(handle);
...@@ -2259,7 +2253,7 @@ static int ext4_symlink(struct inode *dir, ...@@ -2259,7 +2253,7 @@ static int ext4_symlink(struct inode *dir,
retry: retry:
handle = ext4_journal_start(dir, EXT4_DATA_TRANS_BLOCKS(dir->i_sb) + handle = ext4_journal_start(dir, EXT4_DATA_TRANS_BLOCKS(dir->i_sb) +
EXT4_INDEX_EXTRA_TRANS_BLOCKS + 5 + EXT4_INDEX_EXTRA_TRANS_BLOCKS + 5 +
2*EXT4_QUOTA_INIT_BLOCKS(dir->i_sb)); EXT4_MAXQUOTAS_INIT_BLOCKS(dir->i_sb));
if (IS_ERR(handle)) if (IS_ERR(handle))
return PTR_ERR(handle); return PTR_ERR(handle);
......
...@@ -247,7 +247,7 @@ static int setup_new_group_blocks(struct super_block *sb, ...@@ -247,7 +247,7 @@ static int setup_new_group_blocks(struct super_block *sb,
goto exit_bh; goto exit_bh;
if (IS_ERR(gdb = bclean(handle, sb, block))) { if (IS_ERR(gdb = bclean(handle, sb, block))) {
err = PTR_ERR(bh); err = PTR_ERR(gdb);
goto exit_bh; goto exit_bh;
} }
ext4_handle_dirty_metadata(handle, NULL, gdb); ext4_handle_dirty_metadata(handle, NULL, gdb);
......
...@@ -603,10 +603,6 @@ static void ext4_put_super(struct super_block *sb) ...@@ -603,10 +603,6 @@ static void ext4_put_super(struct super_block *sb)
if (sb->s_dirt) if (sb->s_dirt)
ext4_commit_super(sb, 1); ext4_commit_super(sb, 1);
ext4_release_system_zone(sb);
ext4_mb_release(sb);
ext4_ext_release(sb);
ext4_xattr_put_super(sb);
if (sbi->s_journal) { if (sbi->s_journal) {
err = jbd2_journal_destroy(sbi->s_journal); err = jbd2_journal_destroy(sbi->s_journal);
sbi->s_journal = NULL; sbi->s_journal = NULL;
...@@ -614,6 +610,12 @@ static void ext4_put_super(struct super_block *sb) ...@@ -614,6 +610,12 @@ static void ext4_put_super(struct super_block *sb)
ext4_abort(sb, __func__, ext4_abort(sb, __func__,
"Couldn't clean up the journal"); "Couldn't clean up the journal");
} }
ext4_release_system_zone(sb);
ext4_mb_release(sb);
ext4_ext_release(sb);
ext4_xattr_put_super(sb);
if (!(sb->s_flags & MS_RDONLY)) { if (!(sb->s_flags & MS_RDONLY)) {
EXT4_CLEAR_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_RECOVER); EXT4_CLEAR_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_RECOVER);
es->s_state = cpu_to_le16(sbi->s_mount_state); es->s_state = cpu_to_le16(sbi->s_mount_state);
...@@ -704,6 +706,8 @@ static struct inode *ext4_alloc_inode(struct super_block *sb) ...@@ -704,6 +706,8 @@ static struct inode *ext4_alloc_inode(struct super_block *sb)
spin_lock_init(&(ei->i_block_reservation_lock)); spin_lock_init(&(ei->i_block_reservation_lock));
INIT_LIST_HEAD(&ei->i_aio_dio_complete_list); INIT_LIST_HEAD(&ei->i_aio_dio_complete_list);
ei->cur_aio_dio = NULL; ei->cur_aio_dio = NULL;
ei->i_sync_tid = 0;
ei->i_datasync_tid = 0;
return &ei->vfs_inode; return &ei->vfs_inode;
} }
...@@ -899,6 +903,12 @@ static int ext4_show_options(struct seq_file *seq, struct vfsmount *vfs) ...@@ -899,6 +903,12 @@ static int ext4_show_options(struct seq_file *seq, struct vfsmount *vfs)
if (test_opt(sb, NO_AUTO_DA_ALLOC)) if (test_opt(sb, NO_AUTO_DA_ALLOC))
seq_puts(seq, ",noauto_da_alloc"); seq_puts(seq, ",noauto_da_alloc");
if (test_opt(sb, DISCARD))
seq_puts(seq, ",discard");
if (test_opt(sb, NOLOAD))
seq_puts(seq, ",norecovery");
ext4_show_quota_options(seq, sb); ext4_show_quota_options(seq, sb);
return 0; return 0;
...@@ -1079,7 +1089,8 @@ enum { ...@@ -1079,7 +1089,8 @@ enum {
Opt_usrquota, Opt_grpquota, Opt_i_version, Opt_usrquota, Opt_grpquota, Opt_i_version,
Opt_stripe, Opt_delalloc, Opt_nodelalloc, Opt_stripe, Opt_delalloc, Opt_nodelalloc,
Opt_block_validity, Opt_noblock_validity, Opt_block_validity, Opt_noblock_validity,
Opt_inode_readahead_blks, Opt_journal_ioprio Opt_inode_readahead_blks, Opt_journal_ioprio,
Opt_discard, Opt_nodiscard,
}; };
static const match_table_t tokens = { static const match_table_t tokens = {
...@@ -1104,6 +1115,7 @@ static const match_table_t tokens = { ...@@ -1104,6 +1115,7 @@ static const match_table_t tokens = {
{Opt_acl, "acl"}, {Opt_acl, "acl"},
{Opt_noacl, "noacl"}, {Opt_noacl, "noacl"},
{Opt_noload, "noload"}, {Opt_noload, "noload"},
{Opt_noload, "norecovery"},
{Opt_nobh, "nobh"}, {Opt_nobh, "nobh"},
{Opt_bh, "bh"}, {Opt_bh, "bh"},
{Opt_commit, "commit=%u"}, {Opt_commit, "commit=%u"},
...@@ -1144,6 +1156,8 @@ static const match_table_t tokens = { ...@@ -1144,6 +1156,8 @@ static const match_table_t tokens = {
{Opt_auto_da_alloc, "auto_da_alloc=%u"}, {Opt_auto_da_alloc, "auto_da_alloc=%u"},
{Opt_auto_da_alloc, "auto_da_alloc"}, {Opt_auto_da_alloc, "auto_da_alloc"},
{Opt_noauto_da_alloc, "noauto_da_alloc"}, {Opt_noauto_da_alloc, "noauto_da_alloc"},
{Opt_discard, "discard"},
{Opt_nodiscard, "nodiscard"},
{Opt_err, NULL}, {Opt_err, NULL},
}; };
...@@ -1565,6 +1579,12 @@ static int parse_options(char *options, struct super_block *sb, ...@@ -1565,6 +1579,12 @@ static int parse_options(char *options, struct super_block *sb,
else else
set_opt(sbi->s_mount_opt,NO_AUTO_DA_ALLOC); set_opt(sbi->s_mount_opt,NO_AUTO_DA_ALLOC);
break; break;
case Opt_discard:
set_opt(sbi->s_mount_opt, DISCARD);
break;
case Opt_nodiscard:
clear_opt(sbi->s_mount_opt, DISCARD);
break;
default: default:
ext4_msg(sb, KERN_ERR, ext4_msg(sb, KERN_ERR,
"Unrecognized mount option \"%s\" " "Unrecognized mount option \"%s\" "
...@@ -1673,14 +1693,14 @@ static int ext4_fill_flex_info(struct super_block *sb) ...@@ -1673,14 +1693,14 @@ static int ext4_fill_flex_info(struct super_block *sb)
size_t size; size_t size;
int i; int i;
if (!sbi->s_es->s_log_groups_per_flex) { sbi->s_log_groups_per_flex = sbi->s_es->s_log_groups_per_flex;
groups_per_flex = 1 << sbi->s_log_groups_per_flex;
if (groups_per_flex < 2) {
sbi->s_log_groups_per_flex = 0; sbi->s_log_groups_per_flex = 0;
return 1; return 1;
} }
sbi->s_log_groups_per_flex = sbi->s_es->s_log_groups_per_flex;
groups_per_flex = 1 << sbi->s_log_groups_per_flex;
/* We allocate both existing and potentially added groups */ /* We allocate both existing and potentially added groups */
flex_group_count = ((sbi->s_groups_count + groups_per_flex - 1) + flex_group_count = ((sbi->s_groups_count + groups_per_flex - 1) +
((le16_to_cpu(sbi->s_es->s_reserved_gdt_blocks) + 1) << ((le16_to_cpu(sbi->s_es->s_reserved_gdt_blocks) + 1) <<
...@@ -2721,26 +2741,6 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) ...@@ -2721,26 +2741,6 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
EXT4_HAS_COMPAT_FEATURE(sb, EXT4_FEATURE_COMPAT_HAS_JOURNAL)) { EXT4_HAS_COMPAT_FEATURE(sb, EXT4_FEATURE_COMPAT_HAS_JOURNAL)) {
if (ext4_load_journal(sb, es, journal_devnum)) if (ext4_load_journal(sb, es, journal_devnum))
goto failed_mount3; goto failed_mount3;
if (!(sb->s_flags & MS_RDONLY) &&
EXT4_SB(sb)->s_journal->j_failed_commit) {
ext4_msg(sb, KERN_CRIT, "error: "
"ext4_fill_super: Journal transaction "
"%u is corrupt",
EXT4_SB(sb)->s_journal->j_failed_commit);
if (test_opt(sb, ERRORS_RO)) {
ext4_msg(sb, KERN_CRIT,
"Mounting filesystem read-only");
sb->s_flags |= MS_RDONLY;
EXT4_SB(sb)->s_mount_state |= EXT4_ERROR_FS;
es->s_state |= cpu_to_le16(EXT4_ERROR_FS);
}
if (test_opt(sb, ERRORS_PANIC)) {
EXT4_SB(sb)->s_mount_state |= EXT4_ERROR_FS;
es->s_state |= cpu_to_le16(EXT4_ERROR_FS);
ext4_commit_super(sb, 1);
goto failed_mount4;
}
}
} else if (test_opt(sb, NOLOAD) && !(sb->s_flags & MS_RDONLY) && } else if (test_opt(sb, NOLOAD) && !(sb->s_flags & MS_RDONLY) &&
EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_RECOVER)) { EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_RECOVER)) {
ext4_msg(sb, KERN_ERR, "required journal recovery " ext4_msg(sb, KERN_ERR, "required journal recovery "
...@@ -3668,13 +3668,11 @@ static int ext4_statfs(struct dentry *dentry, struct kstatfs *buf) ...@@ -3668,13 +3668,11 @@ static int ext4_statfs(struct dentry *dentry, struct kstatfs *buf)
buf->f_blocks = ext4_blocks_count(es) - sbi->s_overhead_last; buf->f_blocks = ext4_blocks_count(es) - sbi->s_overhead_last;
buf->f_bfree = percpu_counter_sum_positive(&sbi->s_freeblocks_counter) - buf->f_bfree = percpu_counter_sum_positive(&sbi->s_freeblocks_counter) -
percpu_counter_sum_positive(&sbi->s_dirtyblocks_counter); percpu_counter_sum_positive(&sbi->s_dirtyblocks_counter);
ext4_free_blocks_count_set(es, buf->f_bfree);
buf->f_bavail = buf->f_bfree - ext4_r_blocks_count(es); buf->f_bavail = buf->f_bfree - ext4_r_blocks_count(es);
if (buf->f_bfree < ext4_r_blocks_count(es)) if (buf->f_bfree < ext4_r_blocks_count(es))
buf->f_bavail = 0; buf->f_bavail = 0;
buf->f_files = le32_to_cpu(es->s_inodes_count); buf->f_files = le32_to_cpu(es->s_inodes_count);
buf->f_ffree = percpu_counter_sum_positive(&sbi->s_freeinodes_counter); buf->f_ffree = percpu_counter_sum_positive(&sbi->s_freeinodes_counter);
es->s_free_inodes_count = cpu_to_le32(buf->f_ffree);
buf->f_namelen = EXT4_NAME_LEN; buf->f_namelen = EXT4_NAME_LEN;
fsid = le64_to_cpup((void *)es->s_uuid) ^ fsid = le64_to_cpup((void *)es->s_uuid) ^
le64_to_cpup((void *)es->s_uuid + sizeof(u64)); le64_to_cpup((void *)es->s_uuid + sizeof(u64));
...@@ -3966,6 +3964,58 @@ static int ext4_get_sb(struct file_system_type *fs_type, int flags, ...@@ -3966,6 +3964,58 @@ static int ext4_get_sb(struct file_system_type *fs_type, int flags,
return get_sb_bdev(fs_type, flags, dev_name, data, ext4_fill_super,mnt); return get_sb_bdev(fs_type, flags, dev_name, data, ext4_fill_super,mnt);
} }
#if !defined(CONTIG_EXT2_FS) && !defined(CONFIG_EXT2_FS_MODULE) && defined(CONFIG_EXT4_USE_FOR_EXT23)
static struct file_system_type ext2_fs_type = {
.owner = THIS_MODULE,
.name = "ext2",
.get_sb = ext4_get_sb,
.kill_sb = kill_block_super,
.fs_flags = FS_REQUIRES_DEV,
};
static inline void register_as_ext2(void)
{
int err = register_filesystem(&ext2_fs_type);
if (err)
printk(KERN_WARNING
"EXT4-fs: Unable to register as ext2 (%d)\n", err);
}
static inline void unregister_as_ext2(void)
{
unregister_filesystem(&ext2_fs_type);
}
#else
static inline void register_as_ext2(void) { }
static inline void unregister_as_ext2(void) { }
#endif
#if !defined(CONTIG_EXT3_FS) && !defined(CONFIG_EXT3_FS_MODULE) && defined(CONFIG_EXT4_USE_FOR_EXT23)
static struct file_system_type ext3_fs_type = {
.owner = THIS_MODULE,
.name = "ext3",
.get_sb = ext4_get_sb,
.kill_sb = kill_block_super,
.fs_flags = FS_REQUIRES_DEV,
};
static inline void register_as_ext3(void)
{
int err = register_filesystem(&ext3_fs_type);
if (err)
printk(KERN_WARNING
"EXT4-fs: Unable to register as ext3 (%d)\n", err);
}
static inline void unregister_as_ext3(void)
{
unregister_filesystem(&ext3_fs_type);
}
#else
static inline void register_as_ext3(void) { }
static inline void unregister_as_ext3(void) { }
#endif
static struct file_system_type ext4_fs_type = { static struct file_system_type ext4_fs_type = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.name = "ext4", .name = "ext4",
...@@ -3995,11 +4045,15 @@ static int __init init_ext4_fs(void) ...@@ -3995,11 +4045,15 @@ static int __init init_ext4_fs(void)
err = init_inodecache(); err = init_inodecache();
if (err) if (err)
goto out1; goto out1;
register_as_ext2();
register_as_ext3();
err = register_filesystem(&ext4_fs_type); err = register_filesystem(&ext4_fs_type);
if (err) if (err)
goto out; goto out;
return 0; return 0;
out: out:
unregister_as_ext2();
unregister_as_ext3();
destroy_inodecache(); destroy_inodecache();
out1: out1:
exit_ext4_xattr(); exit_ext4_xattr();
...@@ -4015,6 +4069,8 @@ static int __init init_ext4_fs(void) ...@@ -4015,6 +4069,8 @@ static int __init init_ext4_fs(void)
static void __exit exit_ext4_fs(void) static void __exit exit_ext4_fs(void)
{ {
unregister_as_ext2();
unregister_as_ext3();
unregister_filesystem(&ext4_fs_type); unregister_filesystem(&ext4_fs_type);
destroy_inodecache(); destroy_inodecache();
exit_ext4_xattr(); exit_ext4_xattr();
......
...@@ -482,9 +482,10 @@ ext4_xattr_release_block(handle_t *handle, struct inode *inode, ...@@ -482,9 +482,10 @@ ext4_xattr_release_block(handle_t *handle, struct inode *inode,
ea_bdebug(bh, "refcount now=0; freeing"); ea_bdebug(bh, "refcount now=0; freeing");
if (ce) if (ce)
mb_cache_entry_free(ce); mb_cache_entry_free(ce);
ext4_free_blocks(handle, inode, bh->b_blocknr, 1, 1);
get_bh(bh); get_bh(bh);
ext4_forget(handle, 1, inode, bh, bh->b_blocknr); ext4_free_blocks(handle, inode, bh, 0, 1,
EXT4_FREE_BLOCKS_METADATA |
EXT4_FREE_BLOCKS_FORGET);
} else { } else {
le32_add_cpu(&BHDR(bh)->h_refcount, -1); le32_add_cpu(&BHDR(bh)->h_refcount, -1);
error = ext4_handle_dirty_metadata(handle, inode, bh); error = ext4_handle_dirty_metadata(handle, inode, bh);
...@@ -832,7 +833,8 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode, ...@@ -832,7 +833,8 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode,
new_bh = sb_getblk(sb, block); new_bh = sb_getblk(sb, block);
if (!new_bh) { if (!new_bh) {
getblk_failed: getblk_failed:
ext4_free_blocks(handle, inode, block, 1, 1); ext4_free_blocks(handle, inode, 0, block, 1,
EXT4_FREE_BLOCKS_METADATA);
error = -EIO; error = -EIO;
goto cleanup; goto cleanup;
} }
...@@ -988,6 +990,10 @@ ext4_xattr_set_handle(handle_t *handle, struct inode *inode, int name_index, ...@@ -988,6 +990,10 @@ ext4_xattr_set_handle(handle_t *handle, struct inode *inode, int name_index,
if (error) if (error)
goto cleanup; goto cleanup;
error = ext4_journal_get_write_access(handle, is.iloc.bh);
if (error)
goto cleanup;
if (EXT4_I(inode)->i_state & EXT4_STATE_NEW) { if (EXT4_I(inode)->i_state & EXT4_STATE_NEW) {
struct ext4_inode *raw_inode = ext4_raw_inode(&is.iloc); struct ext4_inode *raw_inode = ext4_raw_inode(&is.iloc);
memset(raw_inode, 0, EXT4_SB(inode->i_sb)->s_inode_size); memset(raw_inode, 0, EXT4_SB(inode->i_sb)->s_inode_size);
...@@ -1013,9 +1019,6 @@ ext4_xattr_set_handle(handle_t *handle, struct inode *inode, int name_index, ...@@ -1013,9 +1019,6 @@ ext4_xattr_set_handle(handle_t *handle, struct inode *inode, int name_index,
if (flags & XATTR_CREATE) if (flags & XATTR_CREATE)
goto cleanup; goto cleanup;
} }
error = ext4_journal_get_write_access(handle, is.iloc.bh);
if (error)
goto cleanup;
if (!value) { if (!value) {
if (!is.s.not_found) if (!is.s.not_found)
error = ext4_xattr_ibody_set(handle, inode, &i, &is); error = ext4_xattr_ibody_set(handle, inode, &i, &is);
......
...@@ -636,6 +636,10 @@ void jbd2_journal_commit_transaction(journal_t *journal) ...@@ -636,6 +636,10 @@ void jbd2_journal_commit_transaction(journal_t *journal)
JBUFFER_TRACE(jh, "ph3: write metadata"); JBUFFER_TRACE(jh, "ph3: write metadata");
flags = jbd2_journal_write_metadata_buffer(commit_transaction, flags = jbd2_journal_write_metadata_buffer(commit_transaction,
jh, &new_jh, blocknr); jh, &new_jh, blocknr);
if (flags < 0) {
jbd2_journal_abort(journal, flags);
continue;
}
set_bit(BH_JWrite, &jh2bh(new_jh)->b_state); set_bit(BH_JWrite, &jh2bh(new_jh)->b_state);
wbuf[bufs++] = jh2bh(new_jh); wbuf[bufs++] = jh2bh(new_jh);
......
...@@ -78,6 +78,7 @@ EXPORT_SYMBOL(jbd2_journal_errno); ...@@ -78,6 +78,7 @@ EXPORT_SYMBOL(jbd2_journal_errno);
EXPORT_SYMBOL(jbd2_journal_ack_err); EXPORT_SYMBOL(jbd2_journal_ack_err);
EXPORT_SYMBOL(jbd2_journal_clear_err); EXPORT_SYMBOL(jbd2_journal_clear_err);
EXPORT_SYMBOL(jbd2_log_wait_commit); EXPORT_SYMBOL(jbd2_log_wait_commit);
EXPORT_SYMBOL(jbd2_log_start_commit);
EXPORT_SYMBOL(jbd2_journal_start_commit); EXPORT_SYMBOL(jbd2_journal_start_commit);
EXPORT_SYMBOL(jbd2_journal_force_commit_nested); EXPORT_SYMBOL(jbd2_journal_force_commit_nested);
EXPORT_SYMBOL(jbd2_journal_wipe); EXPORT_SYMBOL(jbd2_journal_wipe);
...@@ -358,6 +359,10 @@ int jbd2_journal_write_metadata_buffer(transaction_t *transaction, ...@@ -358,6 +359,10 @@ int jbd2_journal_write_metadata_buffer(transaction_t *transaction,
jbd_unlock_bh_state(bh_in); jbd_unlock_bh_state(bh_in);
tmp = jbd2_alloc(bh_in->b_size, GFP_NOFS); tmp = jbd2_alloc(bh_in->b_size, GFP_NOFS);
if (!tmp) {
jbd2_journal_put_journal_head(new_jh);
return -ENOMEM;
}
jbd_lock_bh_state(bh_in); jbd_lock_bh_state(bh_in);
if (jh_in->b_frozen_data) { if (jh_in->b_frozen_data) {
jbd2_free(tmp, bh_in->b_size); jbd2_free(tmp, bh_in->b_size);
...@@ -1248,6 +1253,13 @@ int jbd2_journal_load(journal_t *journal) ...@@ -1248,6 +1253,13 @@ int jbd2_journal_load(journal_t *journal)
if (jbd2_journal_recover(journal)) if (jbd2_journal_recover(journal))
goto recovery_error; goto recovery_error;
if (journal->j_failed_commit) {
printk(KERN_ERR "JBD2: journal transaction %u on %s "
"is corrupt.\n", journal->j_failed_commit,
journal->j_devname);
return -EIO;
}
/* OK, we've finished with the dynamic journal bits: /* OK, we've finished with the dynamic journal bits:
* reinitialise the dynamic contents of the superblock in memory * reinitialise the dynamic contents of the superblock in memory
* and reset them on disk. */ * and reset them on disk. */
......
...@@ -38,7 +38,7 @@ TRACE_EVENT(ext4_free_inode, ...@@ -38,7 +38,7 @@ TRACE_EVENT(ext4_free_inode,
__entry->blocks = inode->i_blocks; __entry->blocks = inode->i_blocks;
), ),
TP_printk("dev %s ino %lu mode %d uid %u gid %u blocks %llu", TP_printk("dev %s ino %lu mode 0%o uid %u gid %u blocks %llu",
jbd2_dev_to_name(__entry->dev), (unsigned long) __entry->ino, jbd2_dev_to_name(__entry->dev), (unsigned long) __entry->ino,
__entry->mode, __entry->uid, __entry->gid, __entry->mode, __entry->uid, __entry->gid,
(unsigned long long) __entry->blocks) (unsigned long long) __entry->blocks)
...@@ -61,7 +61,7 @@ TRACE_EVENT(ext4_request_inode, ...@@ -61,7 +61,7 @@ TRACE_EVENT(ext4_request_inode,
__entry->mode = mode; __entry->mode = mode;
), ),
TP_printk("dev %s dir %lu mode %d", TP_printk("dev %s dir %lu mode 0%o",
jbd2_dev_to_name(__entry->dev), (unsigned long) __entry->dir, jbd2_dev_to_name(__entry->dev), (unsigned long) __entry->dir,
__entry->mode) __entry->mode)
); );
...@@ -85,7 +85,7 @@ TRACE_EVENT(ext4_allocate_inode, ...@@ -85,7 +85,7 @@ TRACE_EVENT(ext4_allocate_inode,
__entry->mode = mode; __entry->mode = mode;
), ),
TP_printk("dev %s ino %lu dir %lu mode %d", TP_printk("dev %s ino %lu dir %lu mode 0%o",
jbd2_dev_to_name(__entry->dev), (unsigned long) __entry->ino, jbd2_dev_to_name(__entry->dev), (unsigned long) __entry->ino,
(unsigned long) __entry->dir, __entry->mode) (unsigned long) __entry->dir, __entry->mode)
); );
...@@ -305,7 +305,6 @@ TRACE_EVENT(ext4_da_writepages_result, ...@@ -305,7 +305,6 @@ TRACE_EVENT(ext4_da_writepages_result,
__field( int, ret ) __field( int, ret )
__field( int, pages_written ) __field( int, pages_written )
__field( long, pages_skipped ) __field( long, pages_skipped )
__field( char, encountered_congestion )
__field( char, more_io ) __field( char, more_io )
__field( char, no_nrwrite_index_update ) __field( char, no_nrwrite_index_update )
__field( pgoff_t, writeback_index ) __field( pgoff_t, writeback_index )
...@@ -317,17 +316,16 @@ TRACE_EVENT(ext4_da_writepages_result, ...@@ -317,17 +316,16 @@ TRACE_EVENT(ext4_da_writepages_result,
__entry->ret = ret; __entry->ret = ret;
__entry->pages_written = pages_written; __entry->pages_written = pages_written;
__entry->pages_skipped = wbc->pages_skipped; __entry->pages_skipped = wbc->pages_skipped;
__entry->encountered_congestion = wbc->encountered_congestion;
__entry->more_io = wbc->more_io; __entry->more_io = wbc->more_io;
__entry->no_nrwrite_index_update = wbc->no_nrwrite_index_update; __entry->no_nrwrite_index_update = wbc->no_nrwrite_index_update;
__entry->writeback_index = inode->i_mapping->writeback_index; __entry->writeback_index = inode->i_mapping->writeback_index;
), ),
TP_printk("dev %s ino %lu ret %d pages_written %d pages_skipped %ld congestion %d more_io %d no_nrwrite_index_update %d writeback_index %lu", TP_printk("dev %s ino %lu ret %d pages_written %d pages_skipped %ld more_io %d no_nrwrite_index_update %d writeback_index %lu",
jbd2_dev_to_name(__entry->dev), jbd2_dev_to_name(__entry->dev),
(unsigned long) __entry->ino, __entry->ret, (unsigned long) __entry->ino, __entry->ret,
__entry->pages_written, __entry->pages_skipped, __entry->pages_written, __entry->pages_skipped,
__entry->encountered_congestion, __entry->more_io, __entry->more_io,
__entry->no_nrwrite_index_update, __entry->no_nrwrite_index_update,
(unsigned long) __entry->writeback_index) (unsigned long) __entry->writeback_index)
); );
...@@ -591,30 +589,32 @@ TRACE_EVENT(ext4_allocate_blocks, ...@@ -591,30 +589,32 @@ TRACE_EVENT(ext4_allocate_blocks,
TRACE_EVENT(ext4_free_blocks, TRACE_EVENT(ext4_free_blocks,
TP_PROTO(struct inode *inode, __u64 block, unsigned long count, TP_PROTO(struct inode *inode, __u64 block, unsigned long count,
int metadata), int flags),
TP_ARGS(inode, block, count, metadata), TP_ARGS(inode, block, count, flags),
TP_STRUCT__entry( TP_STRUCT__entry(
__field( dev_t, dev ) __field( dev_t, dev )
__field( ino_t, ino ) __field( ino_t, ino )
__field( umode_t, mode )
__field( __u64, block ) __field( __u64, block )
__field( unsigned long, count ) __field( unsigned long, count )
__field( int, metadata ) __field( int, flags )
), ),
TP_fast_assign( TP_fast_assign(
__entry->dev = inode->i_sb->s_dev; __entry->dev = inode->i_sb->s_dev;
__entry->ino = inode->i_ino; __entry->ino = inode->i_ino;
__entry->mode = inode->i_mode;
__entry->block = block; __entry->block = block;
__entry->count = count; __entry->count = count;
__entry->metadata = metadata; __entry->flags = flags;
), ),
TP_printk("dev %s ino %lu block %llu count %lu metadata %d", TP_printk("dev %s ino %lu mode 0%o block %llu count %lu flags %d",
jbd2_dev_to_name(__entry->dev), (unsigned long) __entry->ino, jbd2_dev_to_name(__entry->dev), (unsigned long) __entry->ino,
__entry->block, __entry->count, __entry->metadata) __entry->mode, __entry->block, __entry->count,
__entry->flags)
); );
TRACE_EVENT(ext4_sync_file, TRACE_EVENT(ext4_sync_file,
...@@ -848,6 +848,32 @@ TRACE_EVENT(ext4_mballoc_free, ...@@ -848,6 +848,32 @@ TRACE_EVENT(ext4_mballoc_free,
__entry->result_len, __entry->result_logical) __entry->result_len, __entry->result_logical)
); );
TRACE_EVENT(ext4_forget,
TP_PROTO(struct inode *inode, int is_metadata, __u64 block),
TP_ARGS(inode, is_metadata, block),
TP_STRUCT__entry(
__field( dev_t, dev )
__field( ino_t, ino )
__field( umode_t, mode )
__field( int, is_metadata )
__field( __u64, block )
),
TP_fast_assign(
__entry->dev = inode->i_sb->s_dev;
__entry->ino = inode->i_ino;
__entry->mode = inode->i_mode;
__entry->is_metadata = is_metadata;
__entry->block = block;
),
TP_printk("dev %s ino %lu mode 0%o is_metadata %d block %llu",
jbd2_dev_to_name(__entry->dev), (unsigned long) __entry->ino,
__entry->mode, __entry->is_metadata, __entry->block)
);
#endif /* _TRACE_EXT4_H */ #endif /* _TRACE_EXT4_H */
/* This part must be outside protection */ /* This part must be outside protection */
......
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