On Fri, Apr 4, 2014 at 3:52 PM, Konstantinos Skarlatos
<k.skarla...@gmail.com> wrote:
> On 4/4/2014 6:20 μμ, Filipe David Borba Manana wrote:
>>
>> This new send flag makes send calculate first the amount of new file data
>> (in bytes)
>> the send root has relatively to the parent root, or for the case of a
>> non-incremental
>> send, the total amount of file data we will send through the send stream.
>> In other words,
>> it computes the sum of the lengths of all write and clone operations that
>> will be sent
>> through the send stream.
>>
>> This data size value is sent in a new command, named
>> BTRFS_SEND_C_TOTAL_DATA_SIZE, that
>> immediately follows a BTRFS_SEND_C_SUBVOL or BTRFS_SEND_C_SNAPSHOT
>> command, and precedes
>> any command that changes a file or the filesystem hierarchy. Upon
>> receiving a write or
>> clone command, the receiving end can increment a counter by the data
>> length of that
>> command and therefore report progress by comparing the counter's value
>> with the data size
>> value received in the BTRFS_SEND_C_TOTAL_DATA_SIZE command.
>>
>> The approach is simple, before the normal operation of send, do a scan in
>> the file system
>> tree for new inodes and file extent items, just like in send's normal
>> operation, and keep
>> incrementing a counter with new inodes' size and the size of file extents
>> that are going
>> to be written or cloned. This is actually a simpler and more lightweight
>> tree scan/processing
>> than the one we do when sending the changes, as it doesn't process inode
>> references nor does
>> any lookups in the extent tree for example.
>>
>> After modifying btrfs-progs to understand this new command and report
>> progress, here's an
>> example (the -o flag tells btrfs send to pass the new flag to the kernel's
>> send ioctl):
>>
>>      $ btrfs send -o /mnt/sdd/base | btrfs receive /mnt/sdc
>>      At subvol /mnt/sdd/base
>>      At subvol base
>>      About to receive 9211507211 bytes
>>      Subvolume/snapshot /mnt/sdc//base, progress  24.73%, 2278015008 bytes
>> received (9211507211 total bytes)
>>
>>      $ btrfs send -o -p /mnt/sdd/base /mnt/sdd/incr | btrfs receive
>> /mnt/sdc
>>      At subvol /mnt/sdd/incr
>>      At snapshot incr
>>      About to receive 9211747739 bytes
>>      Subvolume/snapshot /mnt/sdc//incr, progress  63.42%, 5843024211 bytes
>> received (9211747739 total bytes)
>
> Hi, as a user of send i can say that this feature is very useful. Is it
> possible to add current speed indication (MB/sec)?

Yes, it is.

>
>
>>
>> Signed-off-by: Filipe David Borba Manana <fdman...@gmail.com>
>> ---
>>   fs/btrfs/send.c            | 194
>> +++++++++++++++++++++++++++++++++++++--------
>>   fs/btrfs/send.h            |   1 +
>>   include/uapi/linux/btrfs.h |  13 ++-
>>   3 files changed, 175 insertions(+), 33 deletions(-)
>>
>> diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c
>> index c81e0d9..fa378c7 100644
>> --- a/fs/btrfs/send.c
>> +++ b/fs/btrfs/send.c
>> @@ -81,7 +81,13 @@ struct clone_root {
>>   #define SEND_CTX_MAX_NAME_CACHE_SIZE 128
>>   #define SEND_CTX_NAME_CACHE_CLEAN_SIZE (SEND_CTX_MAX_NAME_CACHE_SIZE *
>> 2)
>>   +enum btrfs_send_phase {
>> +       SEND_PHASE_STREAM_CHANGES,
>> +       SEND_PHASE_COMPUTE_DATA_SIZE,
>> +};
>> +
>>   struct send_ctx {
>> +       enum btrfs_send_phase phase;
>>         struct file *send_filp;
>>         loff_t send_off;
>>         char *send_buf;
>> @@ -116,6 +122,7 @@ struct send_ctx {
>>         u64 cur_inode_last_extent;
>>         u64 send_progress;
>> +       u64 total_data_size;
>>         struct list_head new_refs;
>>         struct list_head deleted_refs;
>> @@ -687,6 +694,8 @@ static int send_rename(struct send_ctx *sctx,
>>   {
>>         int ret;
>>   +     ASSERT(sctx->phase != SEND_PHASE_COMPUTE_DATA_SIZE);
>> +
>>   verbose_printk("btrfs: send_rename %s -> %s\n", from->start, to->start);
>>         ret = begin_cmd(sctx, BTRFS_SEND_C_RENAME);
>> @@ -711,6 +720,8 @@ static int send_link(struct send_ctx *sctx,
>>   {
>>         int ret;
>>   +     ASSERT(sctx->phase != SEND_PHASE_COMPUTE_DATA_SIZE);
>> +
>>   verbose_printk("btrfs: send_link %s -> %s\n", path->start, lnk->start);
>>         ret = begin_cmd(sctx, BTRFS_SEND_C_LINK);
>> @@ -734,6 +745,8 @@ static int send_unlink(struct send_ctx *sctx, struct
>> fs_path *path)
>>   {
>>         int ret;
>>   +     ASSERT(sctx->phase != SEND_PHASE_COMPUTE_DATA_SIZE);
>> +
>>   verbose_printk("btrfs: send_unlink %s\n", path->start);
>>         ret = begin_cmd(sctx, BTRFS_SEND_C_UNLINK);
>> @@ -756,6 +769,8 @@ static int send_rmdir(struct send_ctx *sctx, struct
>> fs_path *path)
>>   {
>>         int ret;
>>   +     ASSERT(sctx->phase != SEND_PHASE_COMPUTE_DATA_SIZE);
>> +
>>   verbose_printk("btrfs: send_rmdir %s\n", path->start);
>>         ret = begin_cmd(sctx, BTRFS_SEND_C_RMDIR);
>> @@ -2286,6 +2301,9 @@ static int send_truncate(struct send_ctx *sctx, u64
>> ino, u64 gen, u64 size)
>>         int ret = 0;
>>         struct fs_path *p;
>>   +     if (sctx->phase == SEND_PHASE_COMPUTE_DATA_SIZE)
>> +               return 0;
>> +
>>   verbose_printk("btrfs: send_truncate %llu size=%llu\n", ino, size);
>>         p = fs_path_alloc();
>> @@ -2315,6 +2333,8 @@ static int send_chmod(struct send_ctx *sctx, u64
>> ino, u64 gen, u64 mode)
>>         int ret = 0;
>>         struct fs_path *p;
>>   +     ASSERT(sctx->phase != SEND_PHASE_COMPUTE_DATA_SIZE);
>> +
>>   verbose_printk("btrfs: send_chmod %llu mode=%llu\n", ino, mode);
>>         p = fs_path_alloc();
>> @@ -2344,6 +2364,8 @@ static int send_chown(struct send_ctx *sctx, u64
>> ino, u64 gen, u64 uid, u64 gid)
>>         int ret = 0;
>>         struct fs_path *p;
>>   +     ASSERT(sctx->phase != SEND_PHASE_COMPUTE_DATA_SIZE);
>> +
>>   verbose_printk("btrfs: send_chown %llu uid=%llu, gid=%llu\n", ino, uid,
>> gid);
>>         p = fs_path_alloc();
>> @@ -2379,6 +2401,8 @@ static int send_utimes(struct send_ctx *sctx, u64
>> ino, u64 gen)
>>         struct btrfs_key key;
>>         int slot;
>>   +     ASSERT(sctx->phase != SEND_PHASE_COMPUTE_DATA_SIZE);
>> +
>>   verbose_printk("btrfs: send_utimes %llu\n", ino);
>>         p = fs_path_alloc();
>> @@ -2441,6 +2465,8 @@ static int send_create_inode(struct send_ctx *sctx,
>> u64 ino)
>>         u64 mode;
>>         u64 rdev;
>>   +     ASSERT(sctx->phase != SEND_PHASE_COMPUTE_DATA_SIZE);
>> +
>>   verbose_printk("btrfs: send_create_inode %llu\n", ino);
>>         p = fs_path_alloc();
>> @@ -2588,6 +2614,8 @@ static int send_create_inode_if_needed(struct
>> send_ctx *sctx)
>>   {
>>         int ret;
>>   +     ASSERT(sctx->phase != SEND_PHASE_COMPUTE_DATA_SIZE);
>> +
>>         if (S_ISDIR(sctx->cur_inode_mode)) {
>>                 ret = did_create_dir(sctx, sctx->cur_ino);
>>                 if (ret < 0)
>> @@ -2693,6 +2721,8 @@ static int orphanize_inode(struct send_ctx *sctx,
>> u64 ino, u64 gen,
>>         int ret;
>>         struct fs_path *orphan;
>>   +     ASSERT(sctx->phase != SEND_PHASE_COMPUTE_DATA_SIZE);
>> +
>>         orphan = fs_path_alloc();
>>         if (!orphan)
>>                 return -ENOMEM;
>> @@ -3061,6 +3091,8 @@ static int apply_dir_move(struct send_ctx *sctx,
>> struct pending_dir_move *pm)
>>         int ret;
>>         u64 ancestor = 0;
>>   +     ASSERT(sctx->phase != SEND_PHASE_COMPUTE_DATA_SIZE);
>> +
>>         name = fs_path_alloc();
>>         from_path = fs_path_alloc();
>>         if (!name || !from_path) {
>> @@ -3315,6 +3347,9 @@ static int process_recorded_refs(struct send_ctx
>> *sctx, int *pending_move)
>>         int is_orphan = 0;
>>         u64 last_dir_ino_rm = 0;
>>   +     if (sctx->phase == SEND_PHASE_COMPUTE_DATA_SIZE)
>> +               return 0;
>> +
>>   verbose_printk("btrfs: process_recorded_refs %llu\n", sctx->cur_ino);
>>         /*
>> @@ -3823,6 +3858,8 @@ static int process_all_refs(struct send_ctx *sctx,
>>         iterate_inode_ref_t cb;
>>         int pending_move = 0;
>>   +     ASSERT(sctx->phase != SEND_PHASE_COMPUTE_DATA_SIZE);
>> +
>>         path = alloc_path_for_send();
>>         if (!path)
>>                 return -ENOMEM;
>> @@ -4142,6 +4179,8 @@ static int process_all_new_xattrs(struct send_ctx
>> *sctx)
>>         struct extent_buffer *eb;
>>         int slot;
>>   +     ASSERT(sctx->phase != SEND_PHASE_COMPUTE_DATA_SIZE);
>> +
>>         path = alloc_path_for_send();
>>         if (!path)
>>                 return -ENOMEM;
>> @@ -4272,6 +4311,8 @@ static int send_write(struct send_ctx *sctx, u64
>> offset, u32 len)
>>         struct fs_path *p;
>>         ssize_t num_read = 0;
>>   +     ASSERT(sctx->phase != SEND_PHASE_COMPUTE_DATA_SIZE);
>> +
>>         p = fs_path_alloc();
>>         if (!p)
>>                 return -ENOMEM;
>> @@ -4307,6 +4348,22 @@ out:
>>         return num_read;
>>   }
>>   +static int send_total_data_size(struct send_ctx *sctx, u64 data_size)
>> +{
>> +       int ret;
>> +
>> +       ret = begin_cmd(sctx, BTRFS_SEND_C_TOTAL_DATA_SIZE);
>> +       if (ret < 0)
>> +               goto out;
>> +
>> +       TLV_PUT_U64(sctx, BTRFS_SEND_A_SIZE, data_size);
>> +       ret = send_cmd(sctx);
>> +
>> +tlv_put_failure:
>> +out:
>> +       return ret;
>> +}
>> +
>>   /*
>>    * Send a clone command to user space.
>>    */
>> @@ -4318,6 +4375,8 @@ static int send_clone(struct send_ctx *sctx,
>>         struct fs_path *p;
>>         u64 gen;
>>   +     ASSERT(sctx->phase != SEND_PHASE_COMPUTE_DATA_SIZE);
>> +
>>   verbose_printk("btrfs: send_clone offset=%llu, len=%d, clone_root=%llu,
>> "
>>                "clone_inode=%llu, clone_offset=%llu\n", offset, len,
>>                 clone_root->root->objectid, clone_root->ino,
>> @@ -4376,6 +4435,8 @@ static int send_update_extent(struct send_ctx *sctx,
>>         int ret = 0;
>>         struct fs_path *p;
>>   +     ASSERT(sctx->phase != SEND_PHASE_COMPUTE_DATA_SIZE);
>> +
>>         p = fs_path_alloc();
>>         if (!p)
>>                 return -ENOMEM;
>> @@ -4407,6 +4468,11 @@ static int send_hole(struct send_ctx *sctx, u64
>> end)
>>         u64 len;
>>         int ret = 0;
>>   +     if (sctx->phase == SEND_PHASE_COMPUTE_DATA_SIZE) {
>> +               sctx->total_data_size += end - offset;
>> +               return 0;
>> +       }
>> +
>>         p = fs_path_alloc();
>>         if (!p)
>>                 return -ENOMEM;
>> @@ -4470,6 +4536,12 @@ static int send_write_or_clone(struct send_ctx
>> *sctx,
>>                 goto out;
>>         }
>>   +     if (sctx->phase == SEND_PHASE_COMPUTE_DATA_SIZE) {
>> +               if (offset < sctx->cur_inode_size)
>> +                       sctx->total_data_size += len;
>> +               goto out;
>> +       }
>> +
>>         if (clone_root && IS_ALIGNED(offset + len, bs)) {
>>                 ret = send_clone(sctx, offset, len, clone_root);
>>         } else if (sctx->flags & BTRFS_SEND_FLAG_NO_FILE_DATA) {
>> @@ -4803,10 +4875,12 @@ static int process_extent(struct send_ctx *sctx,
>>                 }
>>         }
>>   -     ret = find_extent_clone(sctx, path, key->objectid, key->offset,
>> -                       sctx->cur_inode_size, &found_clone);
>> -       if (ret != -ENOENT && ret < 0)
>> -               goto out;
>> +       if (sctx->phase != SEND_PHASE_COMPUTE_DATA_SIZE) {
>> +               ret = find_extent_clone(sctx, path, key->objectid,
>> key->offset,
>> +                                       sctx->cur_inode_size,
>> &found_clone);
>> +               if (ret != -ENOENT && ret < 0)
>> +                       goto out;
>> +       }
>>         ret = send_write_or_clone(sctx, path, key, found_clone);
>>         if (ret)
>> @@ -4936,6 +5010,9 @@ static int finish_inode_if_needed(struct send_ctx
>> *sctx, int at_end)
>>         if (!at_end && sctx->cmp_key->objectid == sctx->cur_ino)
>>                 goto out;
>>   +     if (sctx->phase == SEND_PHASE_COMPUTE_DATA_SIZE)
>> +               goto truncate_inode;
>> +
>>         ret = get_inode_info(sctx->send_root, sctx->cur_ino, NULL, NULL,
>>                         &left_mode, &left_uid, &left_gid, NULL);
>>         if (ret < 0)
>> @@ -4958,6 +5035,7 @@ static int finish_inode_if_needed(struct send_ctx
>> *sctx, int at_end)
>>                         need_chmod = 1;
>>         }
>>   +truncate_inode:
>>         if (S_ISREG(sctx->cur_inode_mode)) {
>>                 if (need_send_hole(sctx)) {
>>                         if (sctx->cur_inode_last_extent == (u64)-1 ||
>> @@ -4997,7 +5075,8 @@ static int finish_inode_if_needed(struct send_ctx
>> *sctx, int at_end)
>>          * If other directory inodes depended on our current directory
>>          * inode's move/rename, now do their move/rename operations.
>>          */
>> -       if (!is_waiting_for_move(sctx, sctx->cur_ino)) {
>> +       if (sctx->phase != SEND_PHASE_COMPUTE_DATA_SIZE &&
>> +           !is_waiting_for_move(sctx, sctx->cur_ino)) {
>>                 ret = apply_children_dir_moves(sctx);
>>                 if (ret)
>>                         goto out;
>> @@ -5081,7 +5160,8 @@ static int changed_inode(struct send_ctx *sctx,
>>                                 sctx->left_path->nodes[0], left_ii);
>>                 sctx->cur_inode_rdev = btrfs_inode_rdev(
>>                                 sctx->left_path->nodes[0], left_ii);
>> -               if (sctx->cur_ino != BTRFS_FIRST_FREE_OBJECTID)
>> +               if (sctx->cur_ino != BTRFS_FIRST_FREE_OBJECTID &&
>> +                   sctx->phase != SEND_PHASE_COMPUTE_DATA_SIZE)
>>                         ret = send_create_inode_if_needed(sctx);
>>         } else if (result == BTRFS_COMPARE_TREE_DELETED) {
>>                 sctx->cur_inode_gen = right_gen;
>> @@ -5103,17 +5183,19 @@ static int changed_inode(struct send_ctx *sctx,
>>                         /*
>>                          * First, process the inode as if it was deleted.
>>                          */
>> -                       sctx->cur_inode_gen = right_gen;
>> -                       sctx->cur_inode_new = 0;
>> -                       sctx->cur_inode_deleted = 1;
>> -                       sctx->cur_inode_size = btrfs_inode_size(
>> +                       if (sctx->phase != SEND_PHASE_COMPUTE_DATA_SIZE) {
>> +                               sctx->cur_inode_gen = right_gen;
>> +                               sctx->cur_inode_new = 0;
>> +                               sctx->cur_inode_deleted = 1;
>> +                               sctx->cur_inode_size = btrfs_inode_size(
>>                                         sctx->right_path->nodes[0],
>> right_ii);
>> -                       sctx->cur_inode_mode = btrfs_inode_mode(
>> +                               sctx->cur_inode_mode = btrfs_inode_mode(
>>                                         sctx->right_path->nodes[0],
>> right_ii);
>> -                       ret = process_all_refs(sctx,
>> -                                       BTRFS_COMPARE_TREE_DELETED);
>> -                       if (ret < 0)
>> -                               goto out;
>> +                               ret = process_all_refs(sctx,
>> +
>> BTRFS_COMPARE_TREE_DELETED);
>> +                               if (ret < 0)
>> +                                       goto out;
>> +                       }
>>                         /*
>>                          * Now process the inode as if it was new.
>> @@ -5127,29 +5209,38 @@ static int changed_inode(struct send_ctx *sctx,
>>                                         sctx->left_path->nodes[0],
>> left_ii);
>>                         sctx->cur_inode_rdev = btrfs_inode_rdev(
>>                                         sctx->left_path->nodes[0],
>> left_ii);
>> -                       ret = send_create_inode_if_needed(sctx);
>> -                       if (ret < 0)
>> -                               goto out;
>> -
>> -                       ret = process_all_refs(sctx,
>> BTRFS_COMPARE_TREE_NEW);
>> -                       if (ret < 0)
>> -                               goto out;
>> +                       if (sctx->phase != SEND_PHASE_COMPUTE_DATA_SIZE) {
>> +                               ret = send_create_inode_if_needed(sctx);
>> +                               if (ret < 0)
>> +                                       goto out;
>> +                               ret = process_all_refs(sctx,
>> +
>> BTRFS_COMPARE_TREE_NEW);
>> +                               if (ret < 0)
>> +                                       goto out;
>> +                       }
>>                         /*
>>                          * Advance send_progress now as we did not get
>> into
>>                          * process_recorded_refs_if_needed in the new_gen
>> case.
>>                          */
>>                         sctx->send_progress = sctx->cur_ino + 1;
>>   -                     /*
>> -                        * Now process all extents and xattrs of the inode
>> as if
>> -                        * they were all new.
>> -                        */
>> -                       ret = process_all_extents(sctx);
>> -                       if (ret < 0)
>> -                               goto out;
>> -                       ret = process_all_new_xattrs(sctx);
>> -                       if (ret < 0)
>> -                               goto out;
>> +                       if (sctx->phase == SEND_PHASE_COMPUTE_DATA_SIZE) {
>> +                               if (S_ISREG(sctx->cur_inode_mode))
>> +                                       sctx->total_data_size +=
>> +                                               sctx->cur_inode_size;
>> +                               /* TODO: maybe account for xattrs one day
>> too */
>> +                       } else {
>> +                               /*
>> +                                * Now process all extents and xattrs of
>> the
>> +                                * inode as if they were all new.
>> +                                */
>> +                               ret = process_all_extents(sctx);
>> +                               if (ret < 0)
>> +                                       goto out;
>> +                               ret = process_all_new_xattrs(sctx);
>> +                               if (ret < 0)
>> +                                       goto out;
>> +                       }
>>                 } else {
>>                         sctx->cur_inode_gen = left_gen;
>>                         sctx->cur_inode_new = 0;
>> @@ -5183,6 +5274,9 @@ static int changed_ref(struct send_ctx *sctx,
>>         BUG_ON(sctx->cur_ino != sctx->cmp_key->objectid);
>>   +     if (sctx->phase == SEND_PHASE_COMPUTE_DATA_SIZE)
>> +               return 0;
>> +
>>         if (!sctx->cur_inode_new_gen &&
>>             sctx->cur_ino != BTRFS_FIRST_FREE_OBJECTID) {
>>                 if (result == BTRFS_COMPARE_TREE_NEW)
>> @@ -5208,6 +5302,9 @@ static int changed_xattr(struct send_ctx *sctx,
>>         BUG_ON(sctx->cur_ino != sctx->cmp_key->objectid);
>>   +     if (sctx->phase == SEND_PHASE_COMPUTE_DATA_SIZE)
>> +               return 0;
>> +
>>         if (!sctx->cur_inode_new_gen && !sctx->cur_inode_deleted) {
>>                 if (result == BTRFS_COMPARE_TREE_NEW)
>>                         ret = process_new_xattr(sctx);
>> @@ -5317,6 +5414,8 @@ static int changed_cb(struct btrfs_root *left_root,
>>         if (result == BTRFS_COMPARE_TREE_SAME) {
>>                 if (key->type == BTRFS_INODE_REF_KEY ||
>>                     key->type == BTRFS_INODE_EXTREF_KEY) {
>> +                       if (sctx->phase == SEND_PHASE_COMPUTE_DATA_SIZE)
>> +                               return 0;
>>                         ret = compare_refs(sctx, left_path, key);
>>                         if (!ret)
>>                                 return 0;
>> @@ -5468,6 +5567,24 @@ out:
>>         return ret;
>>   }
>>   +static int compute_total_data_size(struct send_ctx *sctx)
>> +{
>> +       int ret;
>> +
>> +       sctx->total_data_size = 0;
>> +
>> +       if (sctx->parent_root) {
>> +               ret = btrfs_compare_trees(sctx->send_root,
>> sctx->parent_root,
>> +                                         changed_cb, sctx);
>> +               if (!ret)
>> +                       ret = finish_inode_if_needed(sctx, 1);
>> +       } else {
>> +               ret = full_send_tree(sctx);
>> +       }
>> +
>> +       return ret;
>> +}
>> +
>>   static int send_subvol(struct send_ctx *sctx)
>>   {
>>         int ret;
>> @@ -5482,6 +5599,19 @@ static int send_subvol(struct send_ctx *sctx)
>>         if (ret < 0)
>>                 goto out;
>>   +     if (sctx->flags & BTRFS_SEND_FLAG_CALCULATE_DATA_SIZE) {
>> +               sctx->phase = SEND_PHASE_COMPUTE_DATA_SIZE;
>> +               ret = compute_total_data_size(sctx);
>> +               if (ret < 0)
>> +                       goto out;
>> +               ret = send_total_data_size(sctx, sctx->total_data_size);
>> +               if (ret < 0)
>> +                       goto out;
>> +               sctx->phase = SEND_PHASE_STREAM_CHANGES;
>> +               sctx->cur_ino = 0;
>> +               sctx->send_progress = 0;
>> +       }
>> +
>>         if (sctx->parent_root) {
>>                 ret = btrfs_compare_trees(sctx->send_root,
>> sctx->parent_root,
>>                                 changed_cb, sctx);
>> diff --git a/fs/btrfs/send.h b/fs/btrfs/send.h
>> index 48d425a..febeb72 100644
>> --- a/fs/btrfs/send.h
>> +++ b/fs/btrfs/send.h
>> @@ -87,6 +87,7 @@ enum btrfs_send_cmd {
>>         BTRFS_SEND_C_END,
>>         BTRFS_SEND_C_UPDATE_EXTENT,
>> +       BTRFS_SEND_C_TOTAL_DATA_SIZE,
>>         __BTRFS_SEND_C_MAX,
>>   };
>>   #define BTRFS_SEND_C_MAX (__BTRFS_SEND_C_MAX - 1)
>> diff --git a/include/uapi/linux/btrfs.h b/include/uapi/linux/btrfs.h
>> index b4d6909..afc1529 100644
>> --- a/include/uapi/linux/btrfs.h
>> +++ b/include/uapi/linux/btrfs.h
>> @@ -464,10 +464,21 @@ struct btrfs_ioctl_received_subvol_args {
>>    */
>>   #define BTRFS_SEND_FLAG_OMIT_END_CMD          0x4
>>   +/*
>> + * Calculate the amount (in bytes) of new file data between the send and
>> + * parent snapshots, or in case of a full send, the total amount of file
>> data
>> + * we will send.
>> + * This corresponds to the sum of the data lengths of each write and
>> clone
>> + * commands that are sent through the send stream. The receiving end can
>> use
>> + * this information to compute progress.
>> + */
>> +#define BTRFS_SEND_FLAG_CALCULATE_DATA_SIZE    0x8
>> +
>>   #define BTRFS_SEND_FLAG_MASK \
>>         (BTRFS_SEND_FLAG_NO_FILE_DATA | \
>>          BTRFS_SEND_FLAG_OMIT_STREAM_HEADER | \
>> -        BTRFS_SEND_FLAG_OMIT_END_CMD)
>> +        BTRFS_SEND_FLAG_OMIT_END_CMD | \
>> +        BTRFS_SEND_FLAG_CALCULATE_DATA_SIZE)
>>     struct btrfs_ioctl_send_args {
>>         __s64 send_fd;                  /* in */
>
>



-- 
Filipe David Manana,

"Reasonable men adapt themselves to the world.
 Unreasonable men adapt the world to themselves.
 That's why all progress depends on unreasonable men."
--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to