char *send_buf;
        u32 send_size;
        u32 send_max_size;
+       /*
+        * Whether BTRFS_SEND_A_DATA attribute was already added to current
+        * command (since protocol v2, data must be the last attribute).
+        */
+       bool put_data;
        u64 flags;      /* 'flags' member of btrfs_ioctl_send_args is u64 */
        /* Protocol version compatibility requested */
        u32 proto;
        int total_len = sizeof(*hdr) + len;
        int left = sctx->send_max_size - sctx->send_size;
 
+       if (WARN_ON_ONCE(sctx->put_data))
+               return -EINVAL;
+
        if (unlikely(left < total_len))
                return -EOVERFLOW;
 
                                        &sctx->send_off);
 
        sctx->send_size = 0;
+       sctx->put_data = false;
 
        return ret;
 }
 
 static int put_data_header(struct send_ctx *sctx, u32 len)
 {
-       struct btrfs_tlv_header *hdr;
+       if (WARN_ON_ONCE(sctx->put_data))
+               return -EINVAL;
+       sctx->put_data = true;
+       if (sctx->proto >= 2) {
+               /*
+                * Since v2, the data attribute header doesn't include a length,
+                * it is implicitly to the end of the command.
+                */
+               if (sctx->send_max_size - sctx->send_size < sizeof(__le16) + len)
+                       return -EOVERFLOW;
+               put_unaligned_le16(BTRFS_SEND_A_DATA, sctx->send_buf + sctx->send_size);
+               sctx->send_size += sizeof(__le16);
+       } else {
+               struct btrfs_tlv_header *hdr;
 
-       if (sctx->send_max_size - sctx->send_size < sizeof(*hdr) + len)
-               return -EOVERFLOW;
-       hdr = (struct btrfs_tlv_header *)(sctx->send_buf + sctx->send_size);
-       put_unaligned_le16(BTRFS_SEND_A_DATA, &hdr->tlv_type);
-       put_unaligned_le16(len, &hdr->tlv_len);
-       sctx->send_size += sizeof(*hdr);
+               if (sctx->send_max_size - sctx->send_size < sizeof(*hdr) + len)
+                       return -EOVERFLOW;
+               hdr = (struct btrfs_tlv_header *)(sctx->send_buf + sctx->send_size);
+               put_unaligned_le16(BTRFS_SEND_A_DATA, &hdr->tlv_type);
+               put_unaligned_le16(len, &hdr->tlv_len);
+               sctx->send_size += sizeof(*hdr);
+       }
        return 0;
 }
 
 
        sctx->clone_roots_cnt = arg->clone_sources_count;
 
-       sctx->send_max_size = BTRFS_SEND_BUF_SIZE_V1;
+       if (sctx->proto >= 2)
+               sctx->send_max_size = ALIGN(SZ_16K + BTRFS_MAX_COMPRESSED, PAGE_SIZE);
+       else
+               sctx->send_max_size = BTRFS_SEND_BUF_SIZE_V1;
+
        sctx->send_buf = kvmalloc(sctx->send_max_size, GFP_KERNEL);
        if (!sctx->send_buf) {
                ret = -ENOMEM;