diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c
old mode 100644
new mode 100755
index fcc8c21..4e1049a
--- a/fs/btrfs/ctree.c
+++ b/fs/btrfs/ctree.c
@@ -5165,6 +5165,135 @@ static int tree_compare_item(struct btrfs_root
*left_root,
 #define ADVANCE 1
 #define ADVANCE_ONLY_NEXT -1

+static /*const*/ struct btrfs_key s_key_tree_end_reached = {
+	.objectid = (u64)-1,
+	.type	  = (u8)-1,
+	.offset   = (u64)-1
+};
+
+/*
+ * Generate a tree comparison checkpoint, suitable for being sent over network,
+ * written to disk etc.
+ * Returns the same 'cp' pointer that was passed.
+ */
+struct btrfs_compare_trees_checkpoint*
+btrfs_compare_trees_gen_checkpoint(struct btrfs_compare_trees_checkpoint *cp,
+							 struct btrfs_key *left_key, int left_end_reached,
+							 struct btrfs_key *right_key, int right_end_reached)
+{
+	if (left_end_reached) {
+		cp->left_key__objectid = cpu_to_le64(s_key_tree_end_reached.objectid);
+		cp->left_key__type     = s_key_tree_end_reached.type;
+		cp->left_key__offset   = cpu_to_le64(s_key_tree_end_reached.offset);
+	} else {
+		cp->left_key__objectid = cpu_to_le64(left_key->objectid);
+		cp->left_key__type     = left_key->type;
+		cp->left_key__offset   = cpu_to_le64(left_key->offset);
+	}
+
+	if (right_end_reached) {
+		cp->right_key__objectid = cpu_to_le64(s_key_tree_end_reached.objectid);
+		cp->right_key__type	    = s_key_tree_end_reached.type;
+		cp->right_key__offset   = cpu_to_le64(s_key_tree_end_reached.offset);
+	} else {
+		cp->right_key__objectid = cpu_to_le64(right_key->objectid);
+		cp->right_key__type	    = right_key->type;
+		cp->right_key__offset   = cpu_to_le64(right_key->offset);
+	}
+
+	return cp;
+}
+
+static int btrfs_compare_trees_rearm_to_tree_end(struct btrfs_root *root,
+	/* Output goes here */
+	struct btrfs_path *path,
+	struct btrfs_key *key, int *level, int *end_reached)
+{
+	int ret = 0;
+	struct btrfs_key search_key = s_key_tree_end_reached;
+
+	ret = btrfs_search_slot(NULL, root, &search_key, path, 0/*ins_len*/,
0/*cow*/);
+	if (ret > 0) {
+		*key = s_key_tree_end_reached;                  /* doesn't really
matter in this case */
+		*level = btrfs_header_level(root->commit_root); /* doesn't really
matter in this case */
+		*end_reached = ADVANCE;
+		ret = 0;
+	} else {
+		ret = -EILSEQ;
+	}
+
+	return ret;
+}
+
+static int btrfs_compare_trees_rearm_to_item(struct btrfs_root *root,
struct btrfs_key *rearm_key,
+	/* Output goes here */
+	struct btrfs_path *path,
+	struct btrfs_key *key, int *level, int *end_reached)
+{
+	int ret = 0;
+	struct btrfs_key search_key = *rearm_key;
+
+	ret = btrfs_search_slot(NULL, root, &search_key, path, 0/*ins_len*/,
0/*cow*/);
+	if (ret != 0) {
+		/* We should be able to find the key */
+		ret = -EILSEQ;
+	} else {
+		*key = *rearm_key;
+		*level = 0; /* We found the item, so we're on level 0 */
+		*end_reached = 0;
+	}
+
+	return ret;
+}
+
+static int btrfs_compare_trees_rearm_cp(struct
btrfs_compare_trees_checkpoint *cp,
+	struct btrfs_root *left_root, struct btrfs_root *right_root,
+	/* Output goes here */
+	struct btrfs_path *left_path, struct btrfs_path *right_path,
+	struct btrfs_key *left_key, int *left_level, int *left_end_reached,
+	struct btrfs_key *right_key, int *right_level, int *right_end_reached)
+{
+	int ret = 0;
+	struct btrfs_key rearm_key;
+
+	/* rearm left tree */
+	rearm_key.objectid = le64_to_cpu(cp->left_key__objectid);
+	rearm_key.type     = cp->left_key__type;
+	rearm_key.offset   = le64_to_cpu(cp->left_key__offset);
+	if (btrfs_comp_cpu_keys(&rearm_key, &s_key_tree_end_reached) == 0) {
+		ret = btrfs_compare_trees_rearm_to_tree_end(left_root,
+				/* output */
+				left_path,
+				left_key, left_level, left_end_reached);
+	} else {
+		ret = btrfs_compare_trees_rearm_to_item(left_root, &rearm_key,
+				/* output */
+				left_path,
+				left_key, left_level, left_end_reached);
+	}
+	if (ret)
+		goto out;
+
+	/* rearm right tree */
+	rearm_key.objectid = le64_to_cpu(cp->right_key__objectid);
+	rearm_key.type	   = cp->right_key__type;
+	rearm_key.offset   = le64_to_cpu(cp->right_key__offset);
+	if (btrfs_comp_cpu_keys(&rearm_key, &s_key_tree_end_reached) == 0) {
+		ret = btrfs_compare_trees_rearm_to_tree_end(right_root,
+				/* output */
+				right_path,
+				right_key, right_level, right_end_reached);
+	} else {
+		ret = btrfs_compare_trees_rearm_to_item(right_root, &rearm_key,
+				/* output */
+				right_path,
+				right_key, right_level, right_end_reached);
+	}
+
+out:
+	return ret;
+}
+
 /*
  * This function compares two trees and calls the provided callback for
  * every changed/new/deleted item it finds.
@@ -5180,6 +5309,7 @@ static int tree_compare_item(struct btrfs_root *left_root,
  */
 int btrfs_compare_trees(struct btrfs_root *left_root,
 			struct btrfs_root *right_root,
+			struct btrfs_compare_trees_checkpoint *tree_cp,
 			btrfs_changed_cb_t changed_cb, void *ctx)
 {
 	int ret;
@@ -5203,6 +5333,7 @@ int btrfs_compare_trees(struct btrfs_root *left_root,
 	u64 left_start_ctransid;
 	u64 right_start_ctransid;
 	u64 ctransid;
+	struct btrfs_compare_trees_checkpoint curr_cp;

 	left_path = btrfs_alloc_path();
 	if (!left_path) {
@@ -5277,30 +5408,44 @@ int btrfs_compare_trees(struct btrfs_root *left_root,
 	 *   the right if possible or go up and right.
 	 */

-	left_level = btrfs_header_level(left_root->commit_root);
-	left_root_level = left_level;
-	left_path->nodes[left_level] = left_root->commit_root;
-	extent_buffer_get(left_path->nodes[left_level]);
+	if (tree_cp == NULL) {
+		left_level = btrfs_header_level(left_root->commit_root);
+		left_root_level = left_level;
+		left_path->nodes[left_level] = left_root->commit_root;
+		extent_buffer_get(left_path->nodes[left_level]);

-	right_level = btrfs_header_level(right_root->commit_root);
-	right_root_level = right_level;
-	right_path->nodes[right_level] = right_root->commit_root;
-	extent_buffer_get(right_path->nodes[right_level]);
+		right_level = btrfs_header_level(right_root->commit_root);
+		right_root_level = right_level;
+		right_path->nodes[right_level] = right_root->commit_root;
+		extent_buffer_get(right_path->nodes[right_level]);

-	if (left_level == 0)
-		btrfs_item_key_to_cpu(left_path->nodes[left_level],
-				&left_key, left_path->slots[left_level]);
-	else
-		btrfs_node_key_to_cpu(left_path->nodes[left_level],
-				&left_key, left_path->slots[left_level]);
-	if (right_level == 0)
-		btrfs_item_key_to_cpu(right_path->nodes[right_level],
-				&right_key, right_path->slots[right_level]);
-	else
-		btrfs_node_key_to_cpu(right_path->nodes[right_level],
-				&right_key, right_path->slots[right_level]);
+		if (left_level == 0)
+			btrfs_item_key_to_cpu(left_path->nodes[left_level],
+					&left_key, left_path->slots[left_level]);
+		else
+			btrfs_node_key_to_cpu(left_path->nodes[left_level],
+					&left_key, left_path->slots[left_level]);
+		if (right_level == 0)
+			btrfs_item_key_to_cpu(right_path->nodes[right_level],
+					&right_key, right_path->slots[right_level]);
+		else
+			btrfs_node_key_to_cpu(right_path->nodes[right_level],
+					&right_key, right_path->slots[right_level]);

-	left_end_reached = right_end_reached = 0;
+		left_end_reached = right_end_reached = 0;
+	} else {
+		ret = btrfs_compare_trees_rearm_cp(tree_cp,
+					left_root, right_root,
+					/* output */
+					left_path, right_path,
+					&left_key, &left_level, &left_end_reached,
+					&right_key, &right_level, &right_end_reached);
+		if (ret)
+			goto out;
+		
+		left_root_level = btrfs_header_level(left_root->commit_root);
+		right_root_level = btrfs_header_level(right_root->commit_root);
+	}
 	advance_left = advance_right = 0;

 	while (1) {
@@ -5384,6 +5529,8 @@ int btrfs_compare_trees(struct btrfs_root *left_root,
 			advance_right = 0;
 		}

+		BUG_ON(!(advance_left == 0 && advance_right == 0));
+
 		if (left_end_reached && right_end_reached) {
 			ret = 0;
 			goto out;
@@ -5393,6 +5540,9 @@ int btrfs_compare_trees(struct btrfs_root *left_root,
 						left_path, right_path,
 						&right_key,
 						BTRFS_COMPARE_TREE_DELETED,
+						btrfs_compare_trees_gen_checkpoint(&curr_cp,
+							NULL/*left_key*/, ADVANCE/*left_end_reached*/,
+							&right_key, 0/*right_end_reached*/),
 						ctx);
 				if (ret < 0)
 					goto out;
@@ -5405,6 +5555,9 @@ int btrfs_compare_trees(struct btrfs_root *left_root,
 						left_path, right_path,
 						&left_key,
 						BTRFS_COMPARE_TREE_NEW,
+						btrfs_compare_trees_gen_checkpoint(&curr_cp,
+							&left_key, 0/*left_end_reached*/,
+							NULL/*right_key*/, ADVANCE/*right_end_reached*/),
 						ctx);
 				if (ret < 0)
 					goto out;
@@ -5414,12 +5567,18 @@ int btrfs_compare_trees(struct btrfs_root *left_root,
 		}

 		if (left_level == 0 && right_level == 0) {
+			/* All the cases here will have the same checkpoint */
+			btrfs_compare_trees_gen_checkpoint(&curr_cp,
+				&left_key, 0/*left_end_reached*/,
+				&right_key, 0/*right_end_reached*/);
+
 			cmp = btrfs_comp_cpu_keys(&left_key, &right_key);
 			if (cmp < 0) {
 				ret = changed_cb(left_root, right_root,
 						left_path, right_path,
 						&left_key,
 						BTRFS_COMPARE_TREE_NEW,
+						&curr_cp,
 						ctx);
 				if (ret < 0)
 					goto out;
@@ -5429,6 +5588,7 @@ int btrfs_compare_trees(struct btrfs_root *left_root,
 						left_path, right_path,
 						&right_key,
 						BTRFS_COMPARE_TREE_DELETED,
+						&curr_cp,
 						ctx);
 				if (ret < 0)
 					goto out;
@@ -5443,6 +5603,7 @@ int btrfs_compare_trees(struct btrfs_root *left_root,
 						left_path, right_path,
 						&left_key,
 						BTRFS_COMPARE_TREE_CHANGED,
+						&curr_cp,
 						ctx);
 					if (ret < 0)
 						goto out;
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
old mode 100644
new mode 100755
index c38734a..a1e4282
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -2945,9 +2945,15 @@ typedef int (*btrfs_changed_cb_t)(struct
btrfs_root *left_root,
 				  struct btrfs_path *right_path,
 				  struct btrfs_key *key,
 				  enum btrfs_compare_tree_result result,
+				  struct btrfs_compare_trees_checkpoint *tree_cp,
 				  void *ctx);
+struct btrfs_compare_trees_checkpoint*
+btrfs_compare_trees_gen_checkpoint(struct btrfs_compare_trees_checkpoint *cp,
+							 struct btrfs_key *left_key, int left_end_reached,
+							 struct btrfs_key *right_key, int right_end_reached);
 int btrfs_compare_trees(struct btrfs_root *left_root,
 			struct btrfs_root *right_root,
+			struct btrfs_compare_trees_checkpoint *tree_cp,
 			btrfs_changed_cb_t cb, void *ctx);
 int btrfs_cow_block(struct btrfs_trans_handle *trans,
 		    struct btrfs_root *root, struct extent_buffer *buf,
diff --git a/fs/btrfs/ioctl.h b/fs/btrfs/ioctl.h
old mode 100644
new mode 100755
index 731e287..60f32bb
--- a/fs/btrfs/ioctl.h
+++ b/fs/btrfs/ioctl.h
@@ -363,11 +363,80 @@ struct btrfs_ioctl_received_subvol_args {
 	__u64	reserved[16];		/* in */
 };

+ /*
+  * This is a tree comparison checkpoint.
+  * This information is enough to rearm the tree comparison process
+  * to restart from the point where it was interrupted.
+  *
+  * This struct needs to be in strict endian and alignment, because
it is sent over network.
+  */
+struct btrfs_compare_trees_checkpoint {
+	__le64 left_key__objectid;
+	__u8   left_key__type;
+	__le64 left_key__offset;
+
+	__le64 right_key__objectid;
+	__u8   right_key__type;
+	__le64 right_key__offset;
+} __attribute__ ((__packed__));
+
+ /*
+  * This structure is used to restart the "send" stream
+  * from some previously checkpointed position.
+  * It needs strict endian and alignment, because it is sent over network.
+  */
+struct btrfs_send_checkpoint {
+	__le32 cp_size_bytes;
+ 	__le32 version;
+
+	/* Tree comparison checkpoint */
+	struct btrfs_compare_trees_checkpoint tree_cmp_cp;
+
+	/* Send context checkpoint */
+	__le64 cur_ino;
+	__le64 cur_inode_gen;
+	__u8   cur_inode_new;
+	__u8   cur_inode_new_gen;
+	__u8   cur_inode_deleted;
+	__le64 cur_inode_size;
+	__le64 cur_inode_mode;
+	__le64 send_progress;
+
+	/*
+	 * Kernel cannot produce a checkpoint between any two commands, so
+	 * this allows us to skip the required number of commands since
+	 * the previous checkpoint.
+	 * Also, if the last command was a large WRITE command, but we can send
+	 * only a part of it, and later send the remainder.
+	 */
+	__le32 n_cmds_since_cp;
+ 	__le32 offset_in_write_cmd_bytes;
+
+} __attribute__ ((__packed__));
+
+#define BTRFS_SEND_CHECKPOINT_VERSION 1
+
+#define BTRFS_HAS_TREE_CMP_CHECKPOINT(tree_cp)           \
+	((le64_to_cpu((tree_cp)->left_key__objectid) != 0) && \
+	 (le64_to_cpu((tree_cp)->right_key__objectid) != 0))
+
+#define BTRFS_HAS_SEND_CHECKPOINT(cp)
BTRFS_HAS_TREE_CMP_CHECKPOINT(&((cp)->tree_cmp_cp))
+
 struct btrfs_ioctl_send_args {
-	__s64 send_fd;			/* in */
+	__u8 __user *send_buffer;                     /* in/out: this buffer
will be filled with commands by kernel */
+	__u32 send_buffer_size_bytes;                 /* in:  the size of
send_buffer */
+	                                              /* out: the amount of
valid data on successful completion */
+
 	__u64 clone_sources_count;	/* in */
 	__u64 __user *clone_sources;	/* in */
 	__u64 parent_root;		/* in */
+
+	struct btrfs_send_checkpoint __user *in_cp;   /* in: checkpoint,
kernel must check the version and size */
+	__u32 in_cp_size_bytes;                       /* in: the size of the
checkpoint buffer */
+
+	struct btrfs_send_checkpoint out_cp;          /* out: the checkpoint
for the next batch of commands */
+	__u8 end_of_data;                             /* out: whether we
have pulled the last batch */
+	
 	__u64 flags;			/* in */
 	__u64 reserved[4];		/* in */
 };
diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c
old mode 100644
new mode 100755
index 760390f..b4bf56c
--- a/fs/btrfs/send.c
+++ b/fs/btrfs/send.c
@@ -77,14 +77,30 @@ struct clone_root {
 #define SEND_CTX_NAME_CACHE_CLEAN_SIZE (SEND_CTX_MAX_NAME_CACHE_SIZE * 2)

 struct send_ctx {
-	struct file *send_filp;
-	loff_t send_off;
 	char *send_buf;
 	u32 send_size;
 	u32 send_max_size;
 	u64 total_send_size;
 	u64 cmd_send_size[BTRFS_SEND_C_MAX + 1];

+	/* How many commands/bytes we need to skip, before we can start
sending new commands */
+	u32 n_cmds_to_skip;
+	u32 offset_in_write_cmd_bytes_to_skip;
+
+	/* Our current checkpoint */
+	struct btrfs_send_checkpoint curr_cp;
+
+	/* User buffer to be filled with data and how much data there already is */
+	u8 __user *out_buffer;
+	u32 out_buffer_size_bytes;
+	u32 out_buffer_data_bytes;
+
+	/*
+	 * We set this to 1, when we cannot put more data into the user buffer,
+	 * and we return -EOVERFLOW.
+	 */
+	u8 out_buffer_full;
+
 	struct vfsmount *mnt;

