diff -urpN linux-stage.orig/fs/ext4/balloc.c linux-stage/fs/ext4/balloc.c
---- linux-stage.orig/fs/ext4/balloc.c 2012-07-02 10:32:05.000000000 -0400
-+++ linux-stage/fs/ext4/balloc.c 2012-07-02 11:15:32.000000000 -0400
+--- linux-stage.orig/fs/ext4/balloc.c 2012-07-02 12:07:23.000000000 -0400
++++ linux-stage/fs/ext4/balloc.c 2012-07-02 12:07:57.000000000 -0400
@@ -97,7 +97,7 @@ unsigned ext4_init_block_bitmap(struct s
/* If checksum is bad mark all blocks used to prevent allocation
* essentially implementing a per-group read-only flag. */
(ext4_fsblk_t)(block + i));
BUFFER_TRACE(bitmap_bh, "bit already cleared");
diff -urpN linux-stage.orig/fs/ext4/dir.c linux-stage/fs/ext4/dir.c
---- linux-stage.orig/fs/ext4/dir.c 2012-07-02 10:32:05.000000000 -0400
-+++ linux-stage/fs/ext4/dir.c 2012-07-02 11:15:32.000000000 -0400
+--- linux-stage.orig/fs/ext4/dir.c 2012-07-02 12:07:23.000000000 -0400
++++ linux-stage/fs/ext4/dir.c 2012-07-02 12:07:57.000000000 -0400
@@ -70,28 +70,29 @@ int ext4_check_dir_entry(const char *fun
const int rlen = ext4_rec_len_from_disk(de->rec_len,
dir->i_sb->s_blocksize);
inode->i_ino,
(unsigned long long) filp->f_pos);
diff -urpN linux-stage.orig/fs/ext4/ext4_extents.h linux-stage/fs/ext4/ext4_extents.h
---- linux-stage.orig/fs/ext4/ext4_extents.h 2012-07-02 10:32:05.000000000 -0400
-+++ linux-stage/fs/ext4/ext4_extents.h 2012-07-02 11:15:32.000000000 -0400
+--- linux-stage.orig/fs/ext4/ext4_extents.h 2012-07-02 12:07:23.000000000 -0400
++++ linux-stage/fs/ext4/ext4_extents.h 2012-07-02 12:07:57.000000000 -0400
@@ -138,7 +138,7 @@ typedef int (*ext_prepare_callback)(stru
#define EXT_REPEAT 2
/*
* EXT_INIT_MAX_LEN is the maximum number of blocks we can have in an
diff -urpN linux-stage.orig/fs/ext4/ext4.h linux-stage/fs/ext4/ext4.h
---- linux-stage.orig/fs/ext4/ext4.h 2012-07-02 10:32:05.000000000 -0400
-+++ linux-stage/fs/ext4/ext4.h 2012-07-02 11:15:32.000000000 -0400
+--- linux-stage.orig/fs/ext4/ext4.h 2012-07-02 12:07:23.000000000 -0400
++++ linux-stage/fs/ext4/ext4.h 2012-07-02 12:10:15.000000000 -0400
@@ -23,6 +23,7 @@
#include <linux/quota.h>
#include <linux/rwsem.h>
extern void ext4_msg(struct super_block *, const char *, const char *, ...)
__attribute__ ((format (printf, 3, 4)));
extern void ext4_grp_locked_error(struct super_block *, ext4_group_t,
+@@ -1774,11 +1828,19 @@ struct ext4_group_info {
+ * 5 free 8-block regions. */
+ };
+
+-#define EXT4_GROUP_INFO_NEED_INIT_BIT 0
++#define EXT4_GROUP_INFO_NEED_INIT_BIT 0
++#define EXT4_GROUP_INFO_WAS_TRIMMED_BIT 1
+
+ #define EXT4_MB_GRP_NEED_INIT(grp) \
+ (test_bit(EXT4_GROUP_INFO_NEED_INIT_BIT, &((grp)->bb_state)))
+
++#define EXT4_MB_GRP_WAS_TRIMMED(grp) \
++ (test_bit(EXT4_GROUP_INFO_WAS_TRIMMED_BIT, &((grp)->bb_state)))
++#define EXT4_MB_GRP_SET_TRIMMED(grp) \
++ (set_bit(EXT4_GROUP_INFO_WAS_TRIMMED_BIT, &((grp)->bb_state)))
++#define EXT4_MB_GRP_CLEAR_TRIMMED(grp) \
++ (clear_bit(EXT4_GROUP_INFO_WAS_TRIMMED_BIT, &((grp)->bb_state)))
++
+ #define EXT4_MAX_CONTENTION 8
+ #define EXT4_CONTENTION_THRESHOLD 2
+
diff -urpN linux-stage.orig/fs/ext4/ext4_jbd2.c linux-stage/fs/ext4/ext4_jbd2.c
---- linux-stage.orig/fs/ext4/ext4_jbd2.c 2012-07-02 10:32:05.000000000 -0400
-+++ linux-stage/fs/ext4/ext4_jbd2.c 2012-07-02 11:15:32.000000000 -0400
+--- linux-stage.orig/fs/ext4/ext4_jbd2.c 2012-07-02 12:07:23.000000000 -0400
++++ linux-stage/fs/ext4/ext4_jbd2.c 2012-07-02 12:07:57.000000000 -0400
@@ -96,7 +96,7 @@ int __ext4_handle_dirty_metadata(const c
if (inode && inode_needs_sync(inode)) {
sync_dirty_buffer(bh);
"inode=%lu, block=%llu",
inode->i_ino,
diff -urpN linux-stage.orig/fs/ext4/extents.c linux-stage/fs/ext4/extents.c
---- linux-stage.orig/fs/ext4/extents.c 2012-07-02 10:32:05.000000000 -0400
-+++ linux-stage/fs/ext4/extents.c 2012-07-02 11:15:32.000000000 -0400
+--- linux-stage.orig/fs/ext4/extents.c 2012-07-02 12:07:23.000000000 -0400
++++ linux-stage/fs/ext4/extents.c 2012-07-02 12:07:57.000000000 -0400
@@ -437,7 +437,7 @@ static int __ext4_ext_check(const char *
return 0;
/*
diff -urpN linux-stage.orig/fs/ext4/ialloc.c linux-stage/fs/ext4/ialloc.c
---- linux-stage.orig/fs/ext4/ialloc.c 2012-07-02 10:32:05.000000000 -0400
-+++ linux-stage/fs/ext4/ialloc.c 2012-07-02 11:15:32.000000000 -0400
+--- linux-stage.orig/fs/ext4/ialloc.c 2012-07-02 12:07:23.000000000 -0400
++++ linux-stage/fs/ext4/ialloc.c 2012-07-02 12:07:57.000000000 -0400
@@ -76,7 +76,7 @@ unsigned ext4_init_inode_bitmap(struct s
/* If checksum is bad mark all blocks and inodes use to prevent
* allocation, essentially implementing a per-group read-only flag. */
printk(KERN_NOTICE "ext4_test_bit(bit=%d, block=%llu) = %d\n",
bit, (unsigned long long)bitmap_bh->b_blocknr,
diff -urpN linux-stage.orig/fs/ext4/inode.c linux-stage/fs/ext4/inode.c
---- linux-stage.orig/fs/ext4/inode.c 2012-07-02 10:32:05.000000000 -0400
-+++ linux-stage/fs/ext4/inode.c 2012-07-02 11:15:32.000000000 -0400
+--- linux-stage.orig/fs/ext4/inode.c 2012-07-02 12:07:23.000000000 -0400
++++ linux-stage/fs/ext4/inode.c 2012-07-02 12:07:57.000000000 -0400
@@ -246,7 +246,7 @@ void ext4_delete_inode(struct inode *ino
inode->i_size = 0;
err = ext4_mark_inode_dirty(handle, inode);
" some EAs or run e2fsck.",
inode->i_ino);
diff -urpN linux-stage.orig/fs/ext4/mballoc.c linux-stage/fs/ext4/mballoc.c
---- linux-stage.orig/fs/ext4/mballoc.c 2012-07-02 10:32:05.000000000 -0400
-+++ linux-stage/fs/ext4/mballoc.c 2012-07-02 11:15:32.000000000 -0400
+--- linux-stage.orig/fs/ext4/mballoc.c 2012-07-02 12:07:23.000000000 -0400
++++ linux-stage/fs/ext4/mballoc.c 2012-07-02 12:07:57.000000000 -0400
@@ -862,8 +862,6 @@ static int ext4_mb_init_cache(struct pag
err = 0;
/*
* This function is called by the jbd2 layer once the commit has finished,
* so we know we can free the blocks that were released with that commit.
-@@ -2583,17 +2598,11 @@ static void release_blocks_on_commit(jou
+@@ -2581,22 +2596,9 @@ static void release_blocks_on_commit(jou
+ mb_debug(1, "gonna free %u blocks in group %u (0x%p):",
+ entry->count, entry->group, entry);
- if (test_opt(sb, DISCARD)) {
- int ret;
+- if (test_opt(sb, DISCARD)) {
+- int ret;
- ext4_fsblk_t discard_block;
-
- discard_block = entry->start_blk +
- if (ret == EOPNOTSUPP) {
- ext4_warning(sb, __func__,
- "discard not supported, disabling");
-+ ret = ext4_issue_discard(sb, entry->group,
-+ entry->start_blk, entry->count);
-+ if (unlikely(ret == -EOPNOTSUPP)) {
-+ ext4_warning(sb, "discard not supported, "
-+ "disabling");
- clear_opt(EXT4_SB(sb)->s_mount_opt, DISCARD);
- }
- }
-@@ -2620,7 +2629,7 @@ static void release_blocks_on_commit(jou
+- clear_opt(EXT4_SB(sb)->s_mount_opt, DISCARD);
+- }
+- }
++ if (test_opt(sb, DISCARD))
++ ext4_issue_discard(sb, entry->group,
++ entry->start_blk, entry->count);
+
+ err = ext4_mb_load_buddy(sb, entry->group, &e4b);
+ /* we expect to find existing buddy because it's pinned */
+@@ -2611,6 +2613,15 @@ static void release_blocks_on_commit(jou
+ rb_erase(&entry->node, &(db->bb_free_root));
+ mb_free_blocks(NULL, &e4b, entry->start_blk, entry->count);
+
++ /*
++ * Clear the trimmed flag for the group so that the next
++ * ext4_trim_fs can trim it.
++ * If the volume is mounted with -o discard, online discard
++ * is supported and the free blocks will be trimmed online.
++ */
++ if (!test_opt(sb, DISCARD))
++ EXT4_MB_GRP_CLEAR_TRIMMED(db);
++
+ if (!db->bb_free_root.rb_node) {
+ /* No more items in the per group rb tree
+ * balance refcounts from ext4_mb_free_metadata()
+@@ -2620,7 +2631,7 @@ static void release_blocks_on_commit(jou
}
ext4_unlock_group(sb, entry->group);
kmem_cache_free(ext4_free_ext_cachep, entry);
}
mb_debug(1, "freed %u blocks in %u structures\n", count, count2);
-@@ -2757,7 +2766,7 @@ ext4_mb_mark_diskspace_used(struct ext4_
+@@ -2757,7 +2768,7 @@ ext4_mb_mark_diskspace_used(struct ext4_
len = ac->ac_b_ex.fe_len;
if (!ext4_data_block_valid(sbi, block, len)) {
"Allocating blocks %llu-%llu which overlap "
"fs metadata\n", block, block+len);
/* File system mounted not to panic on error
-@@ -3671,14 +3680,14 @@ ext4_mb_discard_group_preallocations(str
+@@ -3671,14 +3682,14 @@ ext4_mb_discard_group_preallocations(str
bitmap_bh = ext4_read_block_bitmap(sb, group);
if (bitmap_bh == NULL) {
"information for %u", group);
put_bh(bitmap_bh);
return 0;
-@@ -3852,14 +3861,14 @@ repeat:
+@@ -3852,14 +3863,14 @@ repeat:
err = ext4_mb_load_buddy(sb, group, &e4b);
if (err) {
"bitmap for %u", group);
ext4_mb_unload_buddy(&e4b);
continue;
-@@ -4125,7 +4134,7 @@ ext4_mb_discard_lg_preallocations(struct
+@@ -4125,7 +4136,7 @@ ext4_mb_discard_lg_preallocations(struct
ext4_get_group_no_and_offset(sb, pa->pa_pstart, &group, NULL);
if (ext4_mb_load_buddy(sb, group, &e4b)) {
"information for %u", group);
continue;
}
-@@ -4516,7 +4525,7 @@ void ext4_mb_free_blocks(handle_t *handl
+@@ -4516,7 +4527,7 @@ void ext4_mb_free_blocks(handle_t *handl
if (block < le32_to_cpu(es->s_first_data_block) ||
block + count < block ||
block + count > ext4_blocks_count(es)) {
"Freeing blocks not in datazone - "
"block = %llu, count = %lu", block, count);
goto error_return;
-@@ -4561,7 +4570,7 @@ do_more:
+@@ -4561,7 +4572,7 @@ do_more:
in_range(block + count - 1, ext4_inode_table(sb, gdp),
EXT4_SB(sb)->s_itb_per_group)) {
"Block = %llu, count = %lu", block, count);
/* err = 0. ext4_std_error should be a no op */
diff -urpN linux-stage.orig/fs/ext4/move_extent.c linux-stage/fs/ext4/move_extent.c
---- linux-stage.orig/fs/ext4/move_extent.c 2012-07-02 10:32:05.000000000 -0400
-+++ linux-stage/fs/ext4/move_extent.c 2012-07-02 11:15:32.000000000 -0400
+--- linux-stage.orig/fs/ext4/move_extent.c 2012-07-02 12:07:23.000000000 -0400
++++ linux-stage/fs/ext4/move_extent.c 2012-07-02 12:07:57.000000000 -0400
@@ -152,12 +152,12 @@ mext_check_null_inode(struct inode *inod
int ret = 0;
"Both inodes should not be NULL: "
"inode1 %lu inode2 NULL", inode1->i_ino);
ret = -EIO;
+@@ -483,7 +483,7 @@ mext_leaf_block(handle_t *handle, struct
+
+ o_start = o_end = oext = orig_path[depth].p_ext;
+ oext_alen = ext4_ext_get_actual_len(oext);
+- start_ext.ee_len = end_ext.ee_len = 0;
++ start_ext.ee_block = start_ext.ee_len = end_ext.ee_len = 0;
+
+ new_ext.ee_block = cpu_to_le32(*from);
+ ext4_ext_store_pblock(&new_ext, ext_pblock(dext));
@@ -528,7 +528,7 @@ mext_leaf_block(handle_t *handle, struct
* new_ext |-------|
*/
"sum of replaced: %llu requested: %llu",
*moved_len, len);
diff -urpN linux-stage.orig/fs/ext4/namei.c linux-stage/fs/ext4/namei.c
---- linux-stage.orig/fs/ext4/namei.c 2012-07-02 10:32:05.000000000 -0400
-+++ linux-stage/fs/ext4/namei.c 2012-07-02 11:15:32.000000000 -0400
+--- linux-stage.orig/fs/ext4/namei.c 2012-07-02 12:07:23.000000000 -0400
++++ linux-stage/fs/ext4/namei.c 2012-07-02 12:07:57.000000000 -0400
@@ -394,8 +394,7 @@ dx_probe(const struct qstr *d_name, stru
if (root->info.hash_version != DX_HASH_TEA &&
root->info.hash_version != DX_HASH_HALF_MD4 &&
"invalid rec_len for '..' in inode %lu",
dir->i_ino);
brelse(bh);
-@@ -1588,8 +1594,7 @@ static int ext4_dx_add_entry(handle_t *h
+@@ -1468,10 +1474,22 @@ static int make_indexed_dir(handle_t *ha
+ frame->at = entries;
+ frame->bh = bh;
+ bh = bh2;
++
++ ext4_handle_dirty_metadata(handle, dir, frame->bh);
++ ext4_handle_dirty_metadata(handle, dir, bh);
++
+ de = do_split(handle,dir, &bh, frame, &hinfo, &retval);
+- dx_release (frames);
+- if (!(de))
++ if (!de) {
++ /*
++ * Even if the block split failed, we have to properly write
++ * out all the changes we did so far. Otherwise we can end up
++ * with corrupted filesystem.
++ */
++ ext4_mark_inode_dirty(handle, dir);
++ dx_release(frames);
+ return retval;
++ }
++ dx_release(frames);
+
+ retval = add_dirent_to_buf(handle, dentry, inode, de, bh);
+ brelse(bh);
+@@ -1588,8 +1606,7 @@ static int ext4_dx_add_entry(handle_t *h
if (levels && (dx_get_count(frames->entries) ==
dx_get_limit(frames->entries))) {
err = -ENOSPC;
goto cleanup;
}
-@@ -1943,11 +1948,11 @@ static int empty_dir(struct inode *inode
+@@ -1943,11 +1960,11 @@ static int empty_dir(struct inode *inode
if (inode->i_size < EXT4_DIR_REC_LEN(1) + EXT4_DIR_REC_LEN(2) ||
!(bh = ext4_bread(NULL, inode, 0, 0, &err))) {
if (err)
"bad directory (dir #%lu) - no data block",
inode->i_ino);
return 1;
-@@ -1958,7 +1963,7 @@ static int empty_dir(struct inode *inode
+@@ -1958,7 +1975,7 @@ static int empty_dir(struct inode *inode
!le32_to_cpu(de1->inode) ||
strcmp(".", de->name) ||
strcmp("..", de1->name)) {
"bad directory (dir #%lu) - no `.' or `..'",
inode->i_ino);
brelse(bh);
-@@ -1976,7 +1981,7 @@ static int empty_dir(struct inode *inode
+@@ -1976,7 +1993,7 @@ static int empty_dir(struct inode *inode
offset >> EXT4_BLOCK_SIZE_BITS(sb), 0, &err);
if (!bh) {
if (err)
"error %d reading directory"
" #%lu offset %u",
err, inode->i_ino, offset);
-@@ -2198,7 +2203,7 @@ static int ext4_rmdir(struct inode *dir,
+@@ -2198,7 +2215,7 @@ static int ext4_rmdir(struct inode *dir,
if (retval)
goto end_rmdir;
if (!EXT4_DIR_LINK_EMPTY(inode))
"empty directory has too many links (%d)",
inode->i_nlink);
inode->i_version++;
-@@ -2250,7 +2255,7 @@ static int ext4_unlink(struct inode *dir
+@@ -2250,7 +2267,7 @@ static int ext4_unlink(struct inode *dir
goto end_unlink;
if (!inode->i_nlink) {
"Deleting nonexistent file (%lu), %d",
inode->i_ino, inode->i_nlink);
inode->i_nlink = 1;
-@@ -2497,7 +2502,7 @@ static int ext4_rename(struct inode *old
+@@ -2497,7 +2514,7 @@ static int ext4_rename(struct inode *old
}
}
if (retval) {
old_dir->i_ino, old_dir->i_nlink, retval);
}
diff -urpN linux-stage.orig/fs/ext4/resize.c linux-stage/fs/ext4/resize.c
---- linux-stage.orig/fs/ext4/resize.c 2012-07-02 10:32:05.000000000 -0400
-+++ linux-stage/fs/ext4/resize.c 2012-07-02 11:15:32.000000000 -0400
+--- linux-stage.orig/fs/ext4/resize.c 2012-07-02 12:07:23.000000000 -0400
++++ linux-stage/fs/ext4/resize.c 2012-07-02 12:07:57.000000000 -0400
@@ -48,63 +48,63 @@ static int verify_group_input(struct sup
ext4_get_group_no_and_offset(sb, start, NULL, &offset);
mutex_unlock(&EXT4_SB(sb)->s_resize_lock);
ext4_journal_stop(handle);
diff -urpN linux-stage.orig/fs/ext4/super.c linux-stage/fs/ext4/super.c
---- linux-stage.orig/fs/ext4/super.c 2012-07-02 10:32:05.000000000 -0400
-+++ linux-stage/fs/ext4/super.c 2012-07-02 11:19:32.000000000 -0400
+--- linux-stage.orig/fs/ext4/super.c 2012-07-02 12:07:23.000000000 -0400
++++ linux-stage/fs/ext4/super.c 2012-07-02 12:07:57.000000000 -0400
@@ -48,11 +48,16 @@
#include "acl.h"
#include "mballoc.h"
kset_unregister(ext4_kset);
exit_ext4_system_zone();
diff -urpN linux-stage.orig/fs/ext4/xattr.c linux-stage/fs/ext4/xattr.c
---- linux-stage.orig/fs/ext4/xattr.c 2012-07-02 10:32:05.000000000 -0400
-+++ linux-stage/fs/ext4/xattr.c 2012-07-02 11:15:32.000000000 -0400
+--- linux-stage.orig/fs/ext4/xattr.c 2012-07-02 12:07:23.000000000 -0400
++++ linux-stage/fs/ext4/xattr.c 2012-07-02 12:07:57.000000000 -0400
@@ -227,7 +227,7 @@ ext4_xattr_block_get(struct inode *inode
ea_bdebug(bh, "b_count=%d, refcount=%d",
atomic_read(&(bh->b_count)), le32_to_cpu(BHDR(bh)->h_refcount));