[libvirt] [PATCHv2 0/2] add support for btrfs COW copy

If VIR_STORAGE_VOL_CREATE_REFLINK is specified, try to use btrfs COW copy; v2: check BTRFS_IOC_CLONE in configure.ac error out if COW copy is not supported Chen Hanxiao (2): storage: introduce btrfsCloneFile() for COW copy storage: try to perform btrfs clone if possible configure.ac | 12 +++++++ include/libvirt/libvirt-storage.h | 1 + src/storage/storage_backend.c | 67 +++++++++++++++++++++++++++++++++++---- src/storage/storage_backend_fs.c | 8 +++-- src/storage/storage_driver.c | 4 ++- 5 files changed, 82 insertions(+), 10 deletions(-) -- 2.1.0

Signed-off-by: Chen Hanxiao <chenhanxiao@cn.fujitsu.com> --- configure.ac | 12 ++++++++++++ src/storage/storage_backend.c | 24 ++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/configure.ac b/configure.ac index f370475..2498389 100644 --- a/configure.ac +++ b/configure.ac @@ -2175,6 +2175,18 @@ fi AM_CONDITIONAL([WITH_HYPERV], [test "$with_hyperv" = "yes"]) +dnl +dnl check for kernel headers required by btrfs ioctl +dnl +if test "$with_linux" = "yes"; then + have_btrfs=no + AC_CHECK_HEADER([linux/btrfs.h],[have_btrfs=yes]) + if test "${have_btrfs}" = yes; then + AC_DEFINE([HAVE_BTRFS_IOC_CLONE], 1, + [whether have btrfs CoW clone ioctl]) + fi +fi + dnl Allow perl/python overrides AC_PATH_PROGS([PYTHON], [python2 python]) AC_PATH_PROG([PERL], [perl]) diff --git a/src/storage/storage_backend.c b/src/storage/storage_backend.c index b990a82..d2a664b 100644 --- a/src/storage/storage_backend.c +++ b/src/storage/storage_backend.c @@ -46,6 +46,10 @@ # include <selinux/selinux.h> #endif +#if HAVE_BTRFS_IOC_CLONE +# include <linux/btrfs.h> +#endif + #include "datatypes.h" #include "virerror.h" #include "viralloc.h" @@ -156,6 +160,26 @@ enum { #define READ_BLOCK_SIZE_DEFAULT (1024 * 1024) #define WRITE_BLOCK_SIZE_DEFAULT (4 * 1024) +/* + * Perform the O(1) btrfs clone operation, if possible. + * Upon success, return 0. Otherwise, return -1 and set errno. + */ +#if defined(HAVE_BTRFS_IOC_CLONE) +static inline int +btrfsCloneFile(int dest_fd, int src_fd) +{ + return ioctl(dest_fd, BTRFS_IOC_CLONE, src_fd); +} +#else +static inline int +btrfsCloneFile(int dest_fd ATTRIBUTE_UNUSED, + int src_fd ATTRIBUTE_UNUSED) +{ + errno = ENOTSUP; + return -1; +} +#endif + static int ATTRIBUTE_NONNULL(2) virStorageBackendCopyToFD(virStorageVolDefPtr vol, virStorageVolDefPtr inputvol, -- 2.1.0

On 01/23/2015 11:22 AM, Chen Hanxiao wrote:
Signed-off-by: Chen Hanxiao <chenhanxiao@cn.fujitsu.com> --- configure.ac | 12 ++++++++++++ src/storage/storage_backend.c | 24 ++++++++++++++++++++++++ 2 files changed, 36 insertions(+)
diff --git a/configure.ac b/configure.ac index f370475..2498389 100644 --- a/configure.ac +++ b/configure.ac @@ -2175,6 +2175,18 @@ fi AM_CONDITIONAL([WITH_HYPERV], [test "$with_hyperv" = "yes"])
+dnl +dnl check for kernel headers required by btrfs ioctl +dnl +if test "$with_linux" = "yes"; then
+ have_btrfs=no + AC_CHECK_HEADER([linux/btrfs.h],[have_btrfs=yes]) + if test "${have_btrfs}" = yes; then + AC_DEFINE([HAVE_BTRFS_IOC_CLONE], 1, + [whether have btrfs CoW clone ioctl])
This macro name is misleading (it does not check for clone, just for btrfs.h). Doing just: AC_CHECK_HEADERS([linux/btrfs.h]) will define HAVE_LINUX_BTRFS_H
+ fi +fi + dnl Allow perl/python overrides AC_PATH_PROGS([PYTHON], [python2 python]) AC_PATH_PROG([PERL], [perl]) diff --git a/src/storage/storage_backend.c b/src/storage/storage_backend.c index b990a82..d2a664b 100644 --- a/src/storage/storage_backend.c +++ b/src/storage/storage_backend.c @@ -46,6 +46,10 @@ # include <selinux/selinux.h> #endif
+#if HAVE_BTRFS_IOC_CLONE +# include <linux/btrfs.h> +#endif + #include "datatypes.h" #include "virerror.h" #include "viralloc.h" @@ -156,6 +160,26 @@ enum { #define READ_BLOCK_SIZE_DEFAULT (1024 * 1024) #define WRITE_BLOCK_SIZE_DEFAULT (4 * 1024)
+/* + * Perform the O(1) btrfs clone operation, if possible. + * Upon success, return 0. Otherwise, return -1 and set errno. + */ +#if defined(HAVE_BTRFS_IOC_CLONE)
#if HAVE_LINUX_BTRFS_H
+static inline int +btrfsCloneFile(int dest_fd, int src_fd) +{ + return ioctl(dest_fd, BTRFS_IOC_CLONE, src_fd); +} +#else +static inline int +btrfsCloneFile(int dest_fd ATTRIBUTE_UNUSED, + int src_fd ATTRIBUTE_UNUSED) +{ + errno = ENOTSUP; + return -1; +} +#endif + static int ATTRIBUTE_NONNULL(2) virStorageBackendCopyToFD(virStorageVolDefPtr vol, virStorageVolDefPtr inputvol,
ACK I simplified the configure check and pushed the patch. Jan

-----Original Message----- From: Ján Tomko [mailto:jtomko@redhat.com] Sent: Tuesday, January 27, 2015 8:46 PM To: Chen, Hanxiao/陈 晗霄; libvir-list@redhat.com Subject: Re: [libvirt] [PATCHv2 1/2] storage: introduce btrfsCloneFile() for COW copy
On 01/23/2015 11:22 AM, Chen Hanxiao wrote:
Signed-off-by: Chen Hanxiao <chenhanxiao@cn.fujitsu.com> --- configure.ac | 12 ++++++++++++ src/storage/storage_backend.c | 24 ++++++++++++++++++++++++ 2 files changed, 36 insertions(+)
diff --git a/configure.ac b/configure.ac index f370475..2498389 100644 --- a/configure.ac +++ b/configure.ac @@ -2175,6 +2175,18 @@ fi AM_CONDITIONAL([WITH_HYPERV], [test "$with_hyperv" = "yes"])
+dnl +dnl check for kernel headers required by btrfs ioctl +dnl +if test "$with_linux" = "yes"; then
+ have_btrfs=no + AC_CHECK_HEADER([linux/btrfs.h],[have_btrfs=yes]) + if test "${have_btrfs}" = yes; then + AC_DEFINE([HAVE_BTRFS_IOC_CLONE], 1, + [whether have btrfs CoW clone ioctl])
This macro name is misleading (it does not check for clone, just for btrfs.h).
Doing just: AC_CHECK_HEADERS([linux/btrfs.h]) will define HAVE_LINUX_BTRFS_H
+ fi +fi + dnl Allow perl/python overrides AC_PATH_PROGS([PYTHON], [python2 python]) AC_PATH_PROG([PERL], [perl]) diff --git a/src/storage/storage_backend.c b/src/storage/storage_backend.c index b990a82..d2a664b 100644 --- a/src/storage/storage_backend.c +++ b/src/storage/storage_backend.c @@ -46,6 +46,10 @@ # include <selinux/selinux.h> #endif
+#if HAVE_BTRFS_IOC_CLONE +# include <linux/btrfs.h> +#endif + #include "datatypes.h" #include "virerror.h" #include "viralloc.h" @@ -156,6 +160,26 @@ enum { #define READ_BLOCK_SIZE_DEFAULT (1024 * 1024) #define WRITE_BLOCK_SIZE_DEFAULT (4 * 1024)
+/* + * Perform the O(1) btrfs clone operation, if possible. + * Upon success, return 0. Otherwise, return -1 and set errno. + */ +#if defined(HAVE_BTRFS_IOC_CLONE)
#if HAVE_LINUX_BTRFS_H
+static inline int +btrfsCloneFile(int dest_fd, int src_fd) +{ + return ioctl(dest_fd, BTRFS_IOC_CLONE, src_fd); +} +#else +static inline int +btrfsCloneFile(int dest_fd ATTRIBUTE_UNUSED, + int src_fd ATTRIBUTE_UNUSED) +{ + errno = ENOTSUP; + return -1; +} +#endif + static int ATTRIBUTE_NONNULL(2) virStorageBackendCopyToFD(virStorageVolDefPtr vol, virStorageVolDefPtr inputvol,
ACK
I simplified the configure check and pushed the patch.
Thanks for your kindly help. Regards, - Chen

When creating RAW file, we don't take advantage of clone of btrfs. Try to do a btrfs lightweight copy, or error out. Signed-off-by: Chen Hanxiao <chenhanxiao@cn.fujitsu.com> --- include/libvirt/libvirt-storage.h | 1 + src/storage/storage_backend.c | 43 ++++++++++++++++++++++++++++++++------- src/storage/storage_backend_fs.c | 8 ++++++-- src/storage/storage_driver.c | 4 +++- 4 files changed, 46 insertions(+), 10 deletions(-) diff --git a/include/libvirt/libvirt-storage.h b/include/libvirt/libvirt-storage.h index 1f3087b..453089e 100644 --- a/include/libvirt/libvirt-storage.h +++ b/include/libvirt/libvirt-storage.h @@ -306,6 +306,7 @@ const char* virStorageVolGetKey (virStorageVolPtr vol); typedef enum { VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA = 1 << 0, + VIR_STORAGE_VOL_CREATE_REFLINK = 1 << 1, /* perform a btrfs lightweight copy */ } virStorageVolCreateFlags; virStorageVolPtr virStorageVolCreateXML (virStoragePoolPtr pool, diff --git a/src/storage/storage_backend.c b/src/storage/storage_backend.c index d2a664b..cf13704 100644 --- a/src/storage/storage_backend.c +++ b/src/storage/storage_backend.c @@ -185,7 +185,8 @@ virStorageBackendCopyToFD(virStorageVolDefPtr vol, virStorageVolDefPtr inputvol, int fd, unsigned long long *total, - bool want_sparse) + bool want_sparse, + bool reflink_copy) { int inputfd = -1; int amtread = -1; @@ -224,6 +225,19 @@ virStorageBackendCopyToFD(virStorageVolDefPtr vol, goto cleanup; } + if (reflink_copy) { + if (btrfsCloneFile(fd, inputfd) < 0) { + ret = -errno; + virReportSystemError(errno, + _("failed to clone files from '%s'"), + inputvol->target.path); + goto cleanup; + } else { + VIR_DEBUG("btrfs clone findished."); + goto cleanup; + } + } + while (amtread != 0) { int amtleft; @@ -304,8 +318,11 @@ virStorageBackendCreateBlockFrom(virConnectPtr conn ATTRIBUTE_UNUSED, struct stat st; gid_t gid; uid_t uid; + bool reflink_copy = false; - virCheckFlags(VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA, -1); + virCheckFlags(VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA | + VIR_STORAGE_VOL_CREATE_REFLINK, + -1); if (flags & VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", @@ -314,6 +331,9 @@ virStorageBackendCreateBlockFrom(virConnectPtr conn ATTRIBUTE_UNUSED, goto cleanup; } + if (flags & VIR_STORAGE_VOL_CREATE_REFLINK) + reflink_copy = true; + if ((fd = open(vol->target.path, O_RDWR)) < 0) { virReportSystemError(errno, _("cannot create path '%s'"), @@ -325,7 +345,7 @@ virStorageBackendCreateBlockFrom(virConnectPtr conn ATTRIBUTE_UNUSED, if (inputvol) { int res = virStorageBackendCopyToFD(vol, inputvol, - fd, &remain, false); + fd, &remain, false, reflink_copy); if (res < 0) goto cleanup; } @@ -370,7 +390,8 @@ virStorageBackendCreateBlockFrom(virConnectPtr conn ATTRIBUTE_UNUSED, static int createRawFile(int fd, virStorageVolDefPtr vol, - virStorageVolDefPtr inputvol) + virStorageVolDefPtr inputvol, + bool reflink_copy) { bool need_alloc = true; int ret = 0; @@ -417,7 +438,8 @@ createRawFile(int fd, virStorageVolDefPtr vol, bool want_sparse = !need_alloc || (vol->target.allocation < inputvol->target.capacity); - ret = virStorageBackendCopyToFD(vol, inputvol, fd, &remain, want_sparse); + ret = virStorageBackendCopyToFD(vol, inputvol, fd, &remain, + want_sparse, reflink_copy); if (ret < 0) goto cleanup; } @@ -452,8 +474,11 @@ virStorageBackendCreateRaw(virConnectPtr conn ATTRIBUTE_UNUSED, int ret = -1; int fd = -1; int operation_flags; + bool reflink_copy = false; - virCheckFlags(VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA, -1); + virCheckFlags(VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA | + VIR_STORAGE_VOL_CREATE_REFLINK, + -1); if (flags & VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", @@ -462,6 +487,10 @@ virStorageBackendCreateRaw(virConnectPtr conn ATTRIBUTE_UNUSED, goto cleanup; } + if (flags & VIR_STORAGE_VOL_CREATE_REFLINK) + reflink_copy = true; + + if (vol->target.encryption != NULL) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("storage pool does not support encrypted volumes")); @@ -504,7 +533,7 @@ virStorageBackendCreateRaw(virConnectPtr conn ATTRIBUTE_UNUSED, #endif } - if ((ret = createRawFile(fd, vol, inputvol)) < 0) + if ((ret = createRawFile(fd, vol, inputvol, reflink_copy)) < 0) /* createRawFile already reported the exact error. */ ret = -1; diff --git a/src/storage/storage_backend_fs.c b/src/storage/storage_backend_fs.c index 34f2153..cf30aab 100644 --- a/src/storage/storage_backend_fs.c +++ b/src/storage/storage_backend_fs.c @@ -1105,7 +1105,9 @@ virStorageBackendFileSystemVolBuild(virConnectPtr conn, virStorageVolDefPtr vol, unsigned int flags) { - virCheckFlags(VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA, -1); + virCheckFlags(VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA | + VIR_STORAGE_VOL_CREATE_REFLINK, + -1); return _virStorageBackendFileSystemVolBuild(conn, pool, vol, NULL, flags); } @@ -1120,7 +1122,9 @@ virStorageBackendFileSystemVolBuildFrom(virConnectPtr conn, virStorageVolDefPtr inputvol, unsigned int flags) { - virCheckFlags(VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA, -1); + virCheckFlags(VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA | + VIR_STORAGE_VOL_CREATE_REFLINK, + -1); return _virStorageBackendFileSystemVolBuild(conn, pool, vol, inputvol, flags); } diff --git a/src/storage/storage_driver.c b/src/storage/storage_driver.c index 66dc994..2c7af48 100644 --- a/src/storage/storage_driver.c +++ b/src/storage/storage_driver.c @@ -1775,7 +1775,9 @@ storageVolCreateXMLFrom(virStoragePoolPtr obj, unsigned long long allocation; int buildret; - virCheckFlags(VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA, NULL); + virCheckFlags(VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA | + VIR_STORAGE_VOL_CREATE_REFLINK, + NULL); storageDriverLock(); pool = virStoragePoolObjFindByUUID(&driver->pools, obj->uuid); -- 2.1.0

On 01/23/2015 11:22 AM, Chen Hanxiao wrote:
When creating RAW file, we don't take advantage of clone of btrfs.
Try to do a btrfs lightweight copy, or error out.
Signed-off-by: Chen Hanxiao <chenhanxiao@cn.fujitsu.com> --- include/libvirt/libvirt-storage.h | 1 + src/storage/storage_backend.c | 43 ++++++++++++++++++++++++++++++++------- src/storage/storage_backend_fs.c | 8 ++++++-- src/storage/storage_driver.c | 4 +++- 4 files changed, 46 insertions(+), 10 deletions(-)
ACK and pushed. It would also be nice to add the flag to virsh. Jan

-----Original Message----- From: Ján Tomko [mailto:jtomko@redhat.com] Sent: Tuesday, January 27, 2015 8:46 PM To: Chen, Hanxiao/陈 晗霄; libvir-list@redhat.com Subject: Re: [libvirt] [PATCHv2 2/2] storage: try to perform btrfs clone if possible
On 01/23/2015 11:22 AM, Chen Hanxiao wrote:
When creating RAW file, we don't take advantage of clone of btrfs.
Try to do a btrfs lightweight copy, or error out.
Signed-off-by: Chen Hanxiao <chenhanxiao@cn.fujitsu.com> --- include/libvirt/libvirt-storage.h | 1 + src/storage/storage_backend.c | 43 ++++++++++++++++++++++++++++++++------- src/storage/storage_backend_fs.c | 8 ++++++-- src/storage/storage_driver.c | 4 +++- 4 files changed, 46 insertions(+), 10 deletions(-)
ACK and pushed.
It would also be nice to add the flag to virsh.
Sure, I plan to add that flag to virsh and virt-clone. Thanks, - Chen
participants (3)
-
Chen Hanxiao
-
Chen, Hanxiao
-
Ján Tomko