diff --git a/tune/change-csum.c b/tune/change-csum.c
index 7ae618a433cb..b95a4117b59b 100644
--- a/tune/change-csum.c
+++ b/tune/change-csum.c
@@ -197,9 +197,9 @@ out:
  * item.
  */
 #define CSUM_CHANGE_BYTES_THRESHOLD	(SZ_2M)
-static int generate_new_data_csums(struct btrfs_fs_info *fs_info, u16 new_csum_type)
+static int generate_new_data_csums_range(struct btrfs_fs_info *fs_info, u64 start,
+					 u16 new_csum_type)
 {
-	struct btrfs_root *tree_root = fs_info->tree_root;
 	struct btrfs_root *csum_root = btrfs_csum_root(fs_info, 0);
 	struct btrfs_trans_handle *trans;
 	struct btrfs_path path = { 0 };
@@ -208,7 +208,7 @@ static int generate_new_data_csums(struct btrfs_fs_info *fs_info, u16 new_csum_t
 	void *csum_buffer;
 	u64 converted_bytes = 0;
 	u64 last_csum;
-	u64 cur = 0;
+	u64 cur = start;
 	int ret;
 
 	ret = get_last_csum_bytenr(fs_info, &last_csum);
@@ -221,34 +221,6 @@ static int generate_new_data_csums(struct btrfs_fs_info *fs_info, u16 new_csum_t
 	if (!csum_buffer)
 		return -ENOMEM;
 
-	trans = btrfs_start_transaction(tree_root, 1);
-	if (IS_ERR(trans)) {
-		ret = PTR_ERR(trans);
-		errno = -ret;
-		error("failed to start transaction: %m");
-		goto out;
-	}
-	key.objectid = BTRFS_CSUM_CHANGE_OBJECTID;
-	key.type = BTRFS_TEMPORARY_ITEM_KEY;
-	key.offset = new_csum_type;
-	ret = btrfs_insert_empty_item(trans, tree_root, &path, &key, 0);
-	btrfs_release_path(&path);
-	if (ret < 0) {
-		errno = -ret;
-		error("failed to insert csum change item: %m");
-		btrfs_abort_transaction(trans, ret);
-		goto out;
-	}
-	btrfs_set_super_flags(fs_info->super_copy,
-			      btrfs_super_flags(fs_info->super_copy) |
-			      BTRFS_SUPER_FLAG_CHANGING_DATA_CSUM);
-	ret = btrfs_commit_transaction(trans, tree_root);
-	if (ret < 0) {
-		errno = -ret;
-		error("failed to commit the initial transaction: %m");
-		goto out;
-	}
-
 	trans = btrfs_start_transaction(csum_root,
 			CSUM_CHANGE_BYTES_THRESHOLD / fs_info->sectorsize *
 			new_csum_size);
@@ -321,6 +293,44 @@ out:
 	return ret;
 }
 
+static int generate_new_data_csums(struct btrfs_fs_info *fs_info, u16 new_csum_type)
+{
+	struct btrfs_root *tree_root = fs_info->tree_root;
+	struct btrfs_trans_handle *trans;
+	struct btrfs_path path = { 0 };
+	struct btrfs_key key;
+	int ret;
+
+	trans = btrfs_start_transaction(tree_root, 1);
+	if (IS_ERR(trans)) {
+		ret = PTR_ERR(trans);
+		errno = -ret;
+		error("failed to start transaction: %m");
+		return ret;
+	}
+	key.objectid = BTRFS_CSUM_CHANGE_OBJECTID;
+	key.type = BTRFS_TEMPORARY_ITEM_KEY;
+	key.offset = new_csum_type;
+	ret = btrfs_insert_empty_item(trans, tree_root, &path, &key, 0);
+	btrfs_release_path(&path);
+	if (ret < 0) {
+		errno = -ret;
+		error("failed to insert csum change item: %m");
+		btrfs_abort_transaction(trans, ret);
+		return ret;
+	}
+	btrfs_set_super_flags(fs_info->super_copy,
+			      btrfs_super_flags(fs_info->super_copy) |
+			      BTRFS_SUPER_FLAG_CHANGING_DATA_CSUM);
+	ret = btrfs_commit_transaction(trans, tree_root);
+	if (ret < 0) {
+		errno = -ret;
+		error("failed to commit the initial transaction: %m");
+		return ret;
+	}
+	return generate_new_data_csums_range(fs_info, 0, new_csum_type);
+}
+
 static int delete_old_data_csums(struct btrfs_fs_info *fs_info)
 {
 	struct btrfs_root *csum_root = btrfs_csum_root(fs_info, 0);
@@ -635,6 +645,152 @@ out:
 	return ret;
 }
 
+/*
+ * Get the first and last csum items which has @objectid as their objectid.
+ *
+ * This would be called to handle data csum resume, which may have both old
+ * and new csums co-exist in the same csum tree.
+ *
+ * Return >0 if there is no such EXTENT_CSUM with given @objectid.
+ * Return 0 if there is such EXTENT_CSUM and populate @first_ret and @last_ret.
+ * Return <0 for errors.
+ */
+static int get_csum_items_range(struct btrfs_fs_info *fs_info,
+				u64 objectid, u64 *first_ret, u64 *last_ret,
+				u32 *last_item_size)
+{
+	struct btrfs_root *csum_root = btrfs_csum_root(fs_info, 0);
+	struct btrfs_path path = { 0 };
+	struct btrfs_key key;
+	int ret;
+
+	key.objectid = objectid;
+	key.type = BTRFS_EXTENT_CSUM_KEY;
+	key.offset = 0;
+
+	ret = btrfs_search_slot(NULL, csum_root, &key, &path, 0, 0);
+	if (ret < 0) {
+		errno = -ret;
+		error("failed to search csum tree: %m");
+		return ret;
+	}
+	if (path.slots[0] >= btrfs_header_nritems(path.nodes[0])) {
+		ret = btrfs_next_leaf(csum_root, &path);
+		if (ret < 0) {
+			errno = -ret;
+			error("failed to search csum tree: %m");
+			btrfs_release_path(&path);
+			return ret;
+		}
+		/*
+		 * There is no next leaf, meaning we didn't find any
+		 * csum item with given objectid.
+		 */
+		if (ret > 0) {
+			btrfs_release_path(&path);
+			return ret;
+		}
+	}
+
+	btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
+	btrfs_release_path(&path);
+	if (key.objectid != objectid)
+		return 1;
+	*first_ret = key.offset;
+
+	key.objectid = objectid;
+	key.type = BTRFS_EXTENT_CSUM_KEY;
+	key.offset = (u64)-1;
+
+	ret = btrfs_search_slot(NULL, csum_root, &key, &path, 0, 0);
+	if (ret < 0) {
+		errno = -ret;
+		error("failed to search csum tree: %m");
+		return ret;
+	}
+	assert(ret > 0);
+	ret = btrfs_previous_item(csum_root, &path, objectid,
+				  BTRFS_EXTENT_CSUM_KEY);
+	if (ret < 0) {
+		errno = -ret;
+		error("failed to search csum tree: %m");
+		btrfs_release_path(&path);
+		return ret;
+	}
+	if (ret > 0) {
+		btrfs_release_path(&path);
+		return 1;
+	}
+	btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
+	*last_item_size = btrfs_item_size(path.nodes[0], path.slots[0]);
+	btrfs_release_path(&path);
+	*last_ret = key.offset;
+	return 0;
+}
+
+static int resume_data_csum_change(struct btrfs_fs_info *fs_info,
+				   u16 new_csum_type)
+{
+	u64 old_csum_first;
+	u64 old_csum_last;
+	u64 new_csum_first;
+	u64 new_csum_last;
+	bool old_csum_found = false;
+	bool new_csum_found = false;
+	u32 old_last_size;
+	u32 new_last_size;
+	u64 resume_start;
+	int ret;
+
+	ret = get_csum_items_range(fs_info, BTRFS_EXTENT_CSUM_OBJECTID,
+				   &old_csum_first, &old_csum_last,
+				   &old_last_size);
+	if (ret < 0)
+		return ret;
+	if (ret == 0)
+		old_csum_found = true;
+	ret = get_csum_items_range(fs_info, BTRFS_CSUM_CHANGE_OBJECTID,
+				   &new_csum_first, &new_csum_last,
+				   &new_last_size);
+	if (ret < 0)
+		return ret;
+	if (ret == 0)
+		new_csum_found = true;
+
+	/*
+	 * Both old and new csum exist, and new csum is only a subset of the
+	 * old ones.
+	 *
+	 * This means we're still generating new data csums.
+	 */
+	if (old_csum_found && new_csum_found && old_csum_first <= new_csum_first &&
+	    old_csum_last >= new_csum_last) {
+		resume_start = new_csum_last + new_last_size /
+				btrfs_csum_type_size(new_csum_type) *
+				fs_info->sectorsize;
+		goto new_data_csums;
+	}
+
+	/* Other cases are not yet supported. */
+	return -EOPNOTSUPP;
+
+new_data_csums:
+	ret = generate_new_data_csums_range(fs_info, resume_start, new_csum_type);
+	if (ret < 0) {
+		errno = -ret;
+		error("failed to generate new data csums: %m");
+		return ret;
+	}
+	ret = delete_old_data_csums(fs_info);
+	if (ret < 0)
+		return ret;
+	ret = change_csum_objectids(fs_info);
+	if (ret < 0)
+		return ret;
+	ret = change_meta_csums(fs_info, new_csum_type);
+	return ret;
+}
+
 static int resume_csum_change(struct btrfs_fs_info *fs_info, u16 new_csum_type)
 {
 	const u64 super_flags = btrfs_super_flags(fs_info->super_copy);
@@ -683,8 +839,12 @@ static int resume_csum_change(struct btrfs_fs_info *fs_info, u16 new_csum_type)
 	}
 
 	if (super_flags & BTRFS_SUPER_FLAG_CHANGING_DATA_CSUM) {
-		error("resume on data checksum changing is not yet supported");
-		return -EOPNOTSUPP;
+		ret = resume_data_csum_change(fs_info, new_csum_type);
+		if (ret < 0) {
+			errno = -ret;
+			error("failed to resume data checksum change: %m");
+		}
+		return ret;
 	}
 
 	/*
