Difference between revisions of "Documentation/ZfsSend"

Jump to navigation Jump to search
no edit summary
Line 70: Line 70:
But how does ZFS send actually determine the information to be transmitted? How does it construct the records that are actually sent? And how does ZFS recv use those records to reconstruct the original state but on the target pool? Covering these questions in detail and side-by-side with the code will be the focus of the remaining sections. This study will assume some pre-existing knowledge of the ZFS architecture and access to the ZFS code base. ZFS send also provides a number of more advanced options (such as -R or -D), but this walkthrough will focus on the code path taken when doing an incremental send of a single filesystem.  
But how does ZFS send actually determine the information to be transmitted? How does it construct the records that are actually sent? And how does ZFS recv use those records to reconstruct the original state but on the target pool? Covering these questions in detail and side-by-side with the code will be the focus of the remaining sections. This study will assume some pre-existing knowledge of the ZFS architecture and access to the ZFS code base. ZFS send also provides a number of more advanced options (such as -R or -D), but this walkthrough will focus on the code path taken when doing an incremental send of a single filesystem.  


The most common way for a user to interact with ZFS send is through the zfs command line tool, and its send subcommand. Going this route, send-specific code starts at zfs_do_send in zfs_main.c. However, for the simple case we are considering the actual logic begins a little deeper in the call stack, at ''dump_filesystem'' in libzfs_sendrecv.c.
The most common way for a user to interact with ZFS send is through the zfs command line tool, and its send subcommand. Going this route, send-specific code starts at ''zfs_do_send'' in zfs_main.c. However, for the simple case we are considering the actual logic begins a little deeper in the call stack, at ''dump_filesystem'' in libzfs_sendrecv.c.


''dump_filesystem'' passes control down to zfs_iter_snapshots_sorted. zfs_iter_snapshots_sorted’s main responsibility is to sort the snapshots of the target filesystem of this ZFS send operation and iterate over them from earliest to latest. A callback function is called on each snapshot. For the case of ZFS send, this callback is ''dump_snapshot''.
''dump_filesystem'' passes control down to ''zfs_iter_snapshots_sorted''. ''zfs_iter_snapshots_sorted'' ’s main responsibility is to sort the snapshots of the target filesystem of this ZFS send operation and iterate over them from earliest to latest. A callback function is called on each snapshot. For the case of ZFS send, this callback is ''dump_snapshot''.


''dump_snapshot'' filters out any snapshots which are not in the range of snapshots the current ZFS send needs to transmit. For our simplified case of performing an incremental send of a single filesystem, ''dump_snapshot'' iterates to the source snapshot (@before in the original example), saves the name and object ID of that snapshot object, places a hold on that snapshot to ensure it cannot be destroyed while we operate on it, and then iterates to the target snapshot (@after) where it calls ''dump_ioctl''.
''dump_snapshot'' filters out any snapshots which are not in the range of snapshots the current ZFS send needs to transmit. For our simplified case of performing an incremental send of a single filesystem, ''dump_snapshot'' iterates to the source snapshot (@before in the original example), saves the name and object ID of that snapshot object, places a hold on that snapshot to ensure it cannot be destroyed while we operate on it, and then iterates to the target snapshot (@after) where it calls ''dump_ioctl''.


''dump_ioctl'' is where we transition from executing in user space inside the ZFS kernel module. Between ''dump_ioctl'' and the next piece of interesting logic there are several intermediate calls which perform error checking and data retrieval (zfs_ioctl, zfs_ioc_send, ''dmu_send_obj'') but let’s focus a little farther down the stack at ''dmu_send_impl'' in 'dmu_send.c where it really gets interesting.
''dump_ioctl'' is where we transition from executing in user space inside the ZFS kernel module. Between ''dump_ioctl'' and the next piece of interesting logic there are several intermediate calls which perform error checking and data retrieval (''zfs_ioctl'', ''zfs_ioc_send'', ''dmu_send_obj'') but let’s focus a little farther down the stack at ''dmu_send_impl'' in 'dmu_send.c where it really gets interesting.


''dmu_send_impl'' is the first place where we begin writing to the actual ZFS send stream. For instance, ''dmu_send_impl'' passes the in-memory representation of the BEGIN and END records into ''dump_bytes'' for output to the ZFS send stream. The BEGIN record includes identifying info on the source and target snapshots in an incremental send, the name of the dataset being sent, and timestamp time on the target snapshot. The END record includes a checksum of the entire send stream and identifying info on the target snapshot. Even more important, ''dmu_send_impl'' also performs traversal of the current dataset by combining traverse_dataset with a callback, backup_cb.
''dmu_send_impl'' is the first place where we begin writing to the actual ZFS send stream. For instance, ''dmu_send_impl'' passes the in-memory representation of the BEGIN and END records into ''dump_bytes'' for output to the ZFS send stream. The BEGIN record includes identifying info on the source and target snapshots in an incremental send, the name of the dataset being sent, and timestamp time on the target snapshot. The END record includes a checksum of the entire send stream and identifying info on the target snapshot. Even more important, ''dmu_send_impl'' also performs traversal of the current dataset by combining traverse_dataset with a callback, backup_cb.
Line 104: Line 104:
So far we’ve covered the full stack trace from requesting a send using the zfs command line tool down to the ''dump_bytes'' function which does the actual write of ZFS send records from the kernel. However, ZFS send normally (though not necessarily) has a matching ZFS recv which takes the send stream and applies the changes defined by the contained records to some other ZFS dataset. How does that work?
So far we’ve covered the full stack trace from requesting a send using the zfs command line tool down to the ''dump_bytes'' function which does the actual write of ZFS send records from the kernel. However, ZFS send normally (though not necessarily) has a matching ZFS recv which takes the send stream and applies the changes defined by the contained records to some other ZFS dataset. How does that work?


Similar to ZFS send, the most common interface to ZFS recv is through the zfs command line utility with the recv/receive subcommand. The core logic of ZFS receive is located in the kernel down a stack trace such as: zfs_do_receive => zfs_receive => zfs_receive_impl => zfs_receive_one => zfs_ioctl(ZFS_IOC_RECV) => zfs_ioc_recv. Of course, these intermediate functions are necessary to perform setup and error handling, but they aren’t particularly interesting for this overview of the core functionality of ZFS receive. (TODO: I’m kind of punting on this at the moment so I can focus on the interesting code in ''dmu_recv_begin'', ''dmu_recv_stream'', and ''dmu_recv_end'' but I should probably go back and mention some things about the functions that are higher in the stack. I’m also getting a little tired of writing so in general receive is probably going to need more fleshing out).
Similar to ZFS send, the most common interface to ZFS recv is through the zfs command line utility with the recv/receive subcommand. The core logic of ZFS receive is located in the kernel down a stack trace such as: ''zfs_do_receive'' => ''zfs_receive'' => ''zfs_receive_impl'' => ''zfs_receive_one'' => ''zfs_ioctl(ZFS_IOC_RECV)'' => ''zfs_ioc_recv''. Of course, these intermediate functions are necessary to perform setup and error handling, but they aren’t particularly interesting for this overview of the core functionality of ZFS receive. (TODO: I’m kind of punting on this at the moment so I can focus on the interesting code in ''dmu_recv_begin'', ''dmu_recv_stream'', and ''dmu_recv_end'' but I should probably go back and mention some things about the functions that are higher in the stack. I’m also getting a little tired of writing so in general receive is probably going to need more fleshing out).


The implementation of ZFS receive centers around three functions: ''dmu_recv_begin'', ''dmu_recv_stream'', and ''dmu_recv_end''. ''dmu_recv_begin'' performs setup for receiving the stream’s records based on the information contained in the BEGIN record. This includes either creating a new ZFS dataset for the operations in the stream to be applied to, or creating a clone of an existing filessytem to apply those operations to (in the case of an incremental receive). This work is performed inside a DSL sync task.
The implementation of ZFS receive centers around three functions: ''dmu_recv_begin'', ''dmu_recv_stream'', and ''dmu_recv_end''. ''dmu_recv_begin'' performs setup for receiving the stream’s records based on the information contained in the BEGIN record. This includes either creating a new ZFS dataset for the operations in the stream to be applied to, or creating a clone of an existing filessytem to apply those operations to (in the case of an incremental receive). This work is performed inside a DSL sync task.
Editor
90

edits

Navigation menu