diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index ee2dbbde176e..3e0a39653469 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -2998,9 +2998,9 @@ extern int ext4_zero_partial_blocks(handle_t *handle, struct inode *inode,
 extern vm_fault_t ext4_page_mkwrite(struct vm_fault *vmf);
 extern qsize_t *ext4_get_reserved_space(struct inode *inode);
 extern int ext4_get_projid(struct inode *inode, kprojid_t *projid);
-extern void ext4_da_release_space(struct inode *inode, int to_free);
+extern void ext4_da_release_space(struct inode *inode, unsigned int data_len);
 extern void ext4_da_update_reserve_space(struct inode *inode,
-					int used, int quota_claim);
+					unsigned int data_len, int quota_claim);
 extern int ext4_issue_zeroout(struct inode *inode, ext4_lblk_t lblk,
 			      ext4_fsblk_t pblk, ext4_lblk_t len);
 
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 13036cecbcc0..38c47ce1333b 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -327,53 +327,59 @@ qsize_t *ext4_get_reserved_space(struct inode *inode)
 
 static void __ext4_da_update_reserve_space(const char *where,
 					   struct inode *inode,
-					   int data_len)
+					   unsigned int data_len, int ext_len)
 {
 	struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
 	struct ext4_inode_info *ei = EXT4_I(inode);
 
-	if (unlikely(data_len > ei->i_reserved_data_blocks)) {
-		ext4_warning(inode->i_sb, "%s: ino %lu, clear %d "
-			     "with only %d reserved data blocks",
-			     where, inode->i_ino, data_len,
-			     ei->i_reserved_data_blocks);
+	if (unlikely(data_len > ei->i_reserved_data_blocks ||
+		     ext_len > (long)ei->i_reserved_ext_blocks)) {
+		ext4_warning(inode->i_sb, "%s: ino %lu, clear %d,%d "
+			     "with only %d,%d reserved data blocks",
+			     where, inode->i_ino, data_len, ext_len,
+			     ei->i_reserved_data_blocks,
+			     ei->i_reserved_ext_blocks);
 		WARN_ON(1);
-		data_len = ei->i_reserved_data_blocks;
+		data_len = min(data_len, ei->i_reserved_data_blocks);
+		ext_len = min_t(unsigned int, ext_len, ei->i_reserved_ext_blocks);
 	}
 
 	/* Update per-inode reservations */
 	ei->i_reserved_data_blocks -= data_len;
-	percpu_counter_sub(&sbi->s_dirtyclusters_counter, data_len);
+	ei->i_reserved_ext_blocks -= ext_len;
+	percpu_counter_sub(&sbi->s_dirtyclusters_counter, (s64)data_len + ext_len);
 }
 
 /*
  * Called with i_data_sem down, which is important since we can call
  * ext4_discard_preallocations() from here.
  */
-void ext4_da_update_reserve_space(struct inode *inode,
-				  int used, int quota_claim)
+void ext4_da_update_reserve_space(struct inode *inode, unsigned int data_len,
+				  int quota_claim)
 {
 	struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
 	struct ext4_inode_info *ei = EXT4_I(inode);
+	int ext_len = 0;
 
-	if (!used)
+	if (!data_len)
 		return;
 
 	spin_lock(&ei->i_block_reservation_lock);
-	trace_ext4_da_update_reserve_space(inode, used, quota_claim);
-	__ext4_da_update_reserve_space(__func__, inode, used);
+	trace_ext4_da_update_reserve_space(inode, data_len, ext_len,
+					   quota_claim);
+	__ext4_da_update_reserve_space(__func__, inode, data_len, ext_len);
 	spin_unlock(&ei->i_block_reservation_lock);
 
 	/* Update quota subsystem for data blocks */
 	if (quota_claim)
-		dquot_claim_block(inode, EXT4_C2B(sbi, used));
+		dquot_claim_block(inode, EXT4_C2B(sbi, data_len));
 	else {
 		/*
 		 * We did fallocate with an offset that is already delayed
 		 * allocated. So on delayed allocated writeback we should
 		 * not re-claim the quota for fallocated blocks.
 		 */
-		dquot_release_reservation_block(inode, EXT4_C2B(sbi, used));
+		dquot_release_reservation_block(inode, EXT4_C2B(sbi, data_len));
 	}
 
 	/*
@@ -1484,20 +1490,21 @@ static int ext4_da_reserve_space(struct inode *inode, unsigned int rsv_dlen,
 	return 0;       /* success */
 }
 
-void ext4_da_release_space(struct inode *inode, int to_free)
+void ext4_da_release_space(struct inode *inode, unsigned int data_len)
 {
 	struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
 	struct ext4_inode_info *ei = EXT4_I(inode);
+	int ext_len = 0;
 
-	if (!to_free)
+	if (!data_len)
 		return;		/* Nothing to release, exit */
 
 	spin_lock(&ei->i_block_reservation_lock);
-	trace_ext4_da_release_space(inode, to_free);
-	__ext4_da_update_reserve_space(__func__, inode, to_free);
+	trace_ext4_da_release_space(inode, data_len, ext_len);
+	__ext4_da_update_reserve_space(__func__, inode, data_len, ext_len);
 	spin_unlock(&ei->i_block_reservation_lock);
 
-	dquot_release_reservation_block(inode, EXT4_C2B(sbi, to_free));
+	dquot_release_reservation_block(inode, EXT4_C2B(sbi, data_len));
 }
 
 /*
diff --git a/include/trace/events/ext4.h b/include/trace/events/ext4.h
index 7a9839f2d681..e1e9d7ead20f 100644
--- a/include/trace/events/ext4.h
+++ b/include/trace/events/ext4.h
@@ -1214,15 +1214,19 @@ TRACE_EVENT(ext4_forget,
 );
 
 TRACE_EVENT(ext4_da_update_reserve_space,
-	TP_PROTO(struct inode *inode, int used_blocks, int quota_claim),
+	TP_PROTO(struct inode *inode,
+		 int data_blocks,
+		 int meta_blocks,
+		 int quota_claim),
 
-	TP_ARGS(inode, used_blocks, quota_claim),
+	TP_ARGS(inode, data_blocks, meta_blocks, quota_claim),
 
 	TP_STRUCT__entry(
 		__field(	dev_t,	dev			)
 		__field(	ino_t,	ino			)
 		__field(	__u64,	i_blocks		)
-		__field(	int,	used_blocks		)
+		__field(	int,	data_blocks		)
+		__field(	int,	meta_blocks		)
 		__field(	int,	reserved_data_blocks	)
 		__field(	int,	reserved_ext_blocks	)
 		__field(	int,	quota_claim		)
@@ -1233,19 +1237,20 @@ TRACE_EVENT(ext4_da_update_reserve_space,
 		__entry->dev	= inode->i_sb->s_dev;
 		__entry->ino	= inode->i_ino;
 		__entry->i_blocks = inode->i_blocks;
-		__entry->used_blocks = used_blocks;
+		__entry->data_blocks = data_blocks;
+		__entry->meta_blocks = meta_blocks;
 		__entry->reserved_data_blocks = EXT4_I(inode)->i_reserved_data_blocks;
 		__entry->reserved_ext_blocks = EXT4_I(inode)->i_reserved_ext_blocks;
 		__entry->quota_claim = quota_claim;
 		__entry->mode	= inode->i_mode;
 	),
 
-	TP_printk("dev %d,%d ino %lu mode 0%o i_blocks %llu used_blocks %d "
+	TP_printk("dev %d,%d ino %lu mode 0%o i_blocks %llu data_blocks %d meta_blocks %d "
 		  "reserved_data_blocks %d reserved_ext_blocks %d quota_claim %d",
 		  MAJOR(__entry->dev), MINOR(__entry->dev),
 		  (unsigned long) __entry->ino,
 		  __entry->mode, __entry->i_blocks,
-		  __entry->used_blocks,
+		  __entry->data_blocks, __entry->meta_blocks,
 		  __entry->reserved_data_blocks, __entry->reserved_ext_blocks,
 		  __entry->quota_claim)
 );
@@ -1289,15 +1294,16 @@ TRACE_EVENT(ext4_da_reserve_space,
 );
 
 TRACE_EVENT(ext4_da_release_space,
-	TP_PROTO(struct inode *inode, int freed_blocks),
+	TP_PROTO(struct inode *inode, int freed_blocks, int meta_blocks),
 
-	TP_ARGS(inode, freed_blocks),
+	TP_ARGS(inode, freed_blocks, meta_blocks),
 
 	TP_STRUCT__entry(
 		__field(	dev_t,	dev			)
 		__field(	ino_t,	ino			)
 		__field(	__u64,	i_blocks		)
 		__field(	int,	freed_blocks		)
+		__field(	int,	meta_blocks		)
 		__field(	int,	reserved_data_blocks	)
 		__field(	int,	reserved_ext_blocks	)
 		__field(	__u16,  mode			)
@@ -1308,17 +1314,19 @@ TRACE_EVENT(ext4_da_release_space,
 		__entry->ino	= inode->i_ino;
 		__entry->i_blocks = inode->i_blocks;
 		__entry->freed_blocks = freed_blocks;
+		__entry->meta_blocks = meta_blocks;
 		__entry->reserved_data_blocks = EXT4_I(inode)->i_reserved_data_blocks;
 		__entry->reserved_ext_blocks = EXT4_I(inode)->i_reserved_ext_blocks;
 		__entry->mode	= inode->i_mode;
 	),
 
-	TP_printk("dev %d,%d ino %lu mode 0%o i_blocks %llu freed_blocks %d "
+	TP_printk("dev %d,%d ino %lu mode 0%o i_blocks %llu "
+		  "freed_blocks %d meta_blocks %d"
 		  "reserved_data_blocks %d reserved_ext_blocks %d",
 		  MAJOR(__entry->dev), MINOR(__entry->dev),
 		  (unsigned long) __entry->ino,
 		  __entry->mode, __entry->i_blocks,
-		  __entry->freed_blocks,
+		  __entry->freed_blocks, __entry->meta_blocks,
 		  __entry->reserved_data_blocks,
 		  __entry->reserved_ext_blocks)
 );
