diff mbox

qemu-img create: add -o nocow option

Message ID 1384937429-9950-1-git-send-email-cyliu@suse.com
State New
Headers show

Commit Message

Chunyan Liu Nov. 20, 2013, 8:50 a.m. UTC
Add 'nocow' create option so that users could set nocow flag to newly created
images, which could solve performance issues on btrfs.

Btrfs has terrible performance when hosting VM images, even more when the guest
in those VM are also using btrfs as file system. One way to mitigate this bad
performance is to turn off COW attributes on VM files (since having copy on
write for this kind of data is not useful).

Signed-off-by: Chunyan Liu <cyliu@suse.com>
---

Changes made following thread:
    http://lists.gnu.org/archive/html/qemu-devel/2013-11/msg01743.html
    * change to use -o nocow option instead of setting nocow flag as default
    * change to do IOC_GETFLAGS/IOC_SETFLAGS pair
    * fix the READ conflict in header definition in some file

---
 block/cow.c               |   22 ++++++++++++++++++++++
 block/qcow.c              |   22 ++++++++++++++++++++++
 block/qcow2.c             |   22 ++++++++++++++++++++++
 block/raw-posix.c         |   26 ++++++++++++++++++++++++++
 block/vdi.c               |   30 ++++++++++++++++++++++++++++++
 block/vmdk.c              |   36 ++++++++++++++++++++++++++++++++++--
 include/block/block_int.h |    1 +
 7 files changed, 157 insertions(+), 2 deletions(-)

Comments

Stefan Hajnoczi Nov. 20, 2013, 3:15 p.m. UTC | #1
On Wed, Nov 20, 2013 at 04:50:29PM +0800, Chunyan Liu wrote:
>  block/cow.c               |   22 ++++++++++++++++++++++
>  block/qcow.c              |   22 ++++++++++++++++++++++
>  block/qcow2.c             |   22 ++++++++++++++++++++++

I think you can avoid modifying all the image formats:

.bdrv_create() functions pass options to bdrv_create_file().  Therefore
an image format like qcow2 does not need to parse the nocow option
itself.  Only raw-posix.c:.bdrv_create() needs to know about the nocow
option.

The exception is the block drivers that currently use open(2) directly
instead of bdrv_create_file().  These should be converted to use
bdrv_*() APIs instead of POSIX I/O.  Please either convert them or skip
them (someone will get around to fixing them eventually).

> +    if (nocow) {
> +        QEMUOptionParameter list[] = {
> +            {
> +                .name = BLOCK_OPT_NOCOW,
> +                .type = OPT_FLAG,
> +                .value.n = 1,
> +                .help = "No copy-on-write",
> +                .assigned = true
> +            },
> +            { NULL }
> +        };
> +        options = list;
> +    }

This doesn't look safe to me.  list[] is now out-of-scope (the compiler
can reuse the stack space) and options is a dangling pointer.
Chunyan Liu Nov. 21, 2013, 3:33 a.m. UTC | #2
2013/11/20 Stefan Hajnoczi <stefanha@gmail.com>

> On Wed, Nov 20, 2013 at 04:50:29PM +0800, Chunyan Liu wrote:
> >  block/cow.c               |   22 ++++++++++++++++++++++
> >  block/qcow.c              |   22 ++++++++++++++++++++++
> >  block/qcow2.c             |   22 ++++++++++++++++++++++
>
> I think you can avoid modifying all the image formats:
>
> .bdrv_create() functions pass options to bdrv_create_file().  Therefore
> an image format like qcow2 does not need to parse the nocow option
> itself.  Only raw-posix.c:.bdrv_create() needs to know about the nocow
> option.
>
>
In existing code, options passed to bdrv_create_file contains no option in
fact.

And if we pass all options to bdrv_create_file directly, raw-posix.c:
.bdrv_create() will get NOCOW option but at the same time get SIZE option,
it
will create a file with total size. For cow/qcow/qcow2, I suppose it's not
expected? In current code, bdrv_create_file will create a zero-sized image
for
cow/qcow/qcow2.


> The exception is the block drivers that currently use open(2) directly
> instead of bdrv_create_file().  These should be converted to use
> bdrv_*() APIs instead of POSIX I/O.  Please either convert them or skip
> them (someone will get around to fixing them eventually).
>
> > +    if (nocow) {
> > +        QEMUOptionParameter list[] = {
> > +            {
> > +                .name = BLOCK_OPT_NOCOW,
> > +                .type = OPT_FLAG,
> > +                .value.n = 1,
> > +                .help = "No copy-on-write",
> > +                .assigned = true
> > +            },
> > +            { NULL }
> > +        };
> > +        options = list;
> > +    }
>
> This doesn't look safe to me.  list[] is now out-of-scope (the compiler
> can reuse the stack space) and options is a dangling pointer.
>
>
Stefan Hajnoczi Nov. 21, 2013, 8:51 a.m. UTC | #3
On Thu, Nov 21, 2013 at 11:33:56AM +0800, Chunyan Liu wrote:
> 2013/11/20 Stefan Hajnoczi <stefanha@gmail.com>
> 
> > On Wed, Nov 20, 2013 at 04:50:29PM +0800, Chunyan Liu wrote:
> > >  block/cow.c               |   22 ++++++++++++++++++++++
> > >  block/qcow.c              |   22 ++++++++++++++++++++++
> > >  block/qcow2.c             |   22 ++++++++++++++++++++++
> >
> > I think you can avoid modifying all the image formats:
> >
> > .bdrv_create() functions pass options to bdrv_create_file().  Therefore
> > an image format like qcow2 does not need to parse the nocow option
> > itself.  Only raw-posix.c:.bdrv_create() needs to know about the nocow
> > option.
> >
> >
> In existing code, options passed to bdrv_create_file contains no option in
> fact.
> 
> And if we pass all options to bdrv_create_file directly, raw-posix.c:
> .bdrv_create() will get NOCOW option but at the same time get SIZE option,
> it
> will create a file with total size. For cow/qcow/qcow2, I suppose it's not
> expected? In current code, bdrv_create_file will create a zero-sized image
> for
> cow/qcow/qcow2.

I see what the problem is: the loop that parses options does options++.
So by the time it has processed them the options pointer will be NULL or
point to a terminator option (option->name == NULL).

I was confused because there has been a patch series which changes
.bdrv_create() option parsing.  But I forgot it hasn't been merged.
https://lists.gnu.org/archive/html/qemu-devel/2013-08/msg01695.html

Kevin: Have you looked at .bdrv_create() QEMUOptionParameter removal
recently?  Otherwise I'm inclined to merge Chunyan's patch - we can
always refactor the code later when QEMUOptionParameter is removed.

Stefan
Chunyan Liu Nov. 25, 2013, 8:09 a.m. UTC | #4
ping


2013/11/21 Stefan Hajnoczi <stefanha@gmail.com>

> On Thu, Nov 21, 2013 at 11:33:56AM +0800, Chunyan Liu wrote:
> > 2013/11/20 Stefan Hajnoczi <stefanha@gmail.com>
> >
> > > On Wed, Nov 20, 2013 at 04:50:29PM +0800, Chunyan Liu wrote:
> > > >  block/cow.c               |   22 ++++++++++++++++++++++
> > > >  block/qcow.c              |   22 ++++++++++++++++++++++
> > > >  block/qcow2.c             |   22 ++++++++++++++++++++++
> > >
> > > I think you can avoid modifying all the image formats:
> > >
> > > .bdrv_create() functions pass options to bdrv_create_file().  Therefore
> > > an image format like qcow2 does not need to parse the nocow option
> > > itself.  Only raw-posix.c:.bdrv_create() needs to know about the nocow
> > > option.
> > >
> > >
> > In existing code, options passed to bdrv_create_file contains no option
> in
> > fact.
> >
> > And if we pass all options to bdrv_create_file directly, raw-posix.c:
> > .bdrv_create() will get NOCOW option but at the same time get SIZE
> option,
> > it
> > will create a file with total size. For cow/qcow/qcow2, I suppose it's
> not
> > expected? In current code, bdrv_create_file will create a zero-sized
> image
> > for
> > cow/qcow/qcow2.
>
> I see what the problem is: the loop that parses options does options++.
> So by the time it has processed them the options pointer will be NULL or
> point to a terminator option (option->name == NULL).
>
> I was confused because there has been a patch series which changes
> .bdrv_create() option parsing.  But I forgot it hasn't been merged.
> https://lists.gnu.org/archive/html/qemu-devel/2013-08/msg01695.html
>
> Kevin: Have you looked at .bdrv_create() QEMUOptionParameter removal
> recently?  Otherwise I'm inclined to merge Chunyan's patch - we can
> always refactor the code later when QEMUOptionParameter is removed.
>
> Stefan
>
>
Kevin Wolf Nov. 25, 2013, 9:21 a.m. UTC | #5
Am 21.11.2013 um 09:51 hat Stefan Hajnoczi geschrieben:
> On Thu, Nov 21, 2013 at 11:33:56AM +0800, Chunyan Liu wrote:
> > 2013/11/20 Stefan Hajnoczi <stefanha@gmail.com>
> > 
> > > On Wed, Nov 20, 2013 at 04:50:29PM +0800, Chunyan Liu wrote:
> > > >  block/cow.c               |   22 ++++++++++++++++++++++
> > > >  block/qcow.c              |   22 ++++++++++++++++++++++
> > > >  block/qcow2.c             |   22 ++++++++++++++++++++++
> > >
> > > I think you can avoid modifying all the image formats:
> > >
> > > .bdrv_create() functions pass options to bdrv_create_file().  Therefore
> > > an image format like qcow2 does not need to parse the nocow option
> > > itself.  Only raw-posix.c:.bdrv_create() needs to know about the nocow
> > > option.
> > >
> > >
> > In existing code, options passed to bdrv_create_file contains no option in
> > fact.
> > 
> > And if we pass all options to bdrv_create_file directly, raw-posix.c:
> > .bdrv_create() will get NOCOW option but at the same time get SIZE option,
> > it
> > will create a file with total size. For cow/qcow/qcow2, I suppose it's not
> > expected? In current code, bdrv_create_file will create a zero-sized image
> > for
> > cow/qcow/qcow2.
> 
> I see what the problem is: the loop that parses options does options++.
> So by the time it has processed them the options pointer will be NULL or
> point to a terminator option (option->name == NULL).
> 
> I was confused because there has been a patch series which changes
> .bdrv_create() option parsing.  But I forgot it hasn't been merged.
> https://lists.gnu.org/archive/html/qemu-devel/2013-08/msg01695.html
> 
> Kevin: Have you looked at .bdrv_create() QEMUOptionParameter removal
> recently?  Otherwise I'm inclined to merge Chunyan's patch - we can
> always refactor the code later when QEMUOptionParameter is removed.

No, I haven't. I think the QemuOpts conversion series was relatively
close, but it seems nobody is working on it any more. Someone should
probably pick it up and finish it.

Kevin
Chunyan Liu Dec. 2, 2013, 3:54 a.m. UTC | #6
2013/11/25 Kevin Wolf <kwolf@redhat.com>

> Am 21.11.2013 um 09:51 hat Stefan Hajnoczi geschrieben:
> > On Thu, Nov 21, 2013 at 11:33:56AM +0800, Chunyan Liu wrote:
> > > 2013/11/20 Stefan Hajnoczi <stefanha@gmail.com>
> > >
> > > > On Wed, Nov 20, 2013 at 04:50:29PM +0800, Chunyan Liu wrote:
> > > > >  block/cow.c               |   22 ++++++++++++++++++++++
> > > > >  block/qcow.c              |   22 ++++++++++++++++++++++
> > > > >  block/qcow2.c             |   22 ++++++++++++++++++++++
> > > >
> > > > I think you can avoid modifying all the image formats:
> > > >
> > > > .bdrv_create() functions pass options to bdrv_create_file().
>  Therefore
> > > > an image format like qcow2 does not need to parse the nocow option
> > > > itself.  Only raw-posix.c:.bdrv_create() needs to know about the
> nocow
> > > > option.
> > > >
> > > >
> > > In existing code, options passed to bdrv_create_file contains no
> option in
> > > fact.
> > >
> > > And if we pass all options to bdrv_create_file directly, raw-posix.c:
> > > .bdrv_create() will get NOCOW option but at the same time get SIZE
> option,
> > > it
> > > will create a file with total size. For cow/qcow/qcow2, I suppose it's
> not
> > > expected? In current code, bdrv_create_file will create a zero-sized
> image
> > > for
> > > cow/qcow/qcow2.
> >
> > I see what the problem is: the loop that parses options does options++.
> > So by the time it has processed them the options pointer will be NULL or
> > point to a terminator option (option->name == NULL).
> >
> > I was confused because there has been a patch series which changes
> > .bdrv_create() option parsing.  But I forgot it hasn't been merged.
> > https://lists.gnu.org/archive/html/qemu-devel/2013-08/msg01695.html
> >
> > Kevin: Have you looked at .bdrv_create() QEMUOptionParameter removal
> > recently?  Otherwise I'm inclined to merge Chunyan's patch - we can
> > always refactor the code later when QEMUOptionParameter is removed.
>
> No, I haven't. I think the QemuOpts conversion series was relatively
> close, but it seems nobody is working on it any more. Someone should
> probably pick it up and finish it.
>

Hi, Kevin & Stefan,
About the nocow option, according to current status, how should we proceed?
Any
further work needs to do?


>
> Kevin
>
>
Stefan Hajnoczi Dec. 4, 2013, 9:20 a.m. UTC | #7
On Mon, Dec 2, 2013 at 4:54 AM, Chunyan Liu <cyliu@suse.com> wrote:
> 2013/11/25 Kevin Wolf <kwolf@redhat.com>
>>
>> Am 21.11.2013 um 09:51 hat Stefan Hajnoczi geschrieben:
>> > On Thu, Nov 21, 2013 at 11:33:56AM +0800, Chunyan Liu wrote:
>> > > 2013/11/20 Stefan Hajnoczi <stefanha@gmail.com>
>> > >
>> > > > On Wed, Nov 20, 2013 at 04:50:29PM +0800, Chunyan Liu wrote:
>> > > > >  block/cow.c               |   22 ++++++++++++++++++++++
>> > > > >  block/qcow.c              |   22 ++++++++++++++++++++++
>> > > > >  block/qcow2.c             |   22 ++++++++++++++++++++++
>> > > >
>> > > > I think you can avoid modifying all the image formats:
>> > > >
>> > > > .bdrv_create() functions pass options to bdrv_create_file().
>> > > > Therefore
>> > > > an image format like qcow2 does not need to parse the nocow option
>> > > > itself.  Only raw-posix.c:.bdrv_create() needs to know about the
>> > > > nocow
>> > > > option.
>> > > >
>> > > >
>> > > In existing code, options passed to bdrv_create_file contains no
>> > > option in
>> > > fact.
>> > >
>> > > And if we pass all options to bdrv_create_file directly, raw-posix.c:
>> > > .bdrv_create() will get NOCOW option but at the same time get SIZE
>> > > option,
>> > > it
>> > > will create a file with total size. For cow/qcow/qcow2, I suppose it's
>> > > not
>> > > expected? In current code, bdrv_create_file will create a zero-sized
>> > > image
>> > > for
>> > > cow/qcow/qcow2.
>> >
>> > I see what the problem is: the loop that parses options does options++.
>> > So by the time it has processed them the options pointer will be NULL or
>> > point to a terminator option (option->name == NULL).
>> >
>> > I was confused because there has been a patch series which changes
>> > .bdrv_create() option parsing.  But I forgot it hasn't been merged.
>> > https://lists.gnu.org/archive/html/qemu-devel/2013-08/msg01695.html
>> >
>> > Kevin: Have you looked at .bdrv_create() QEMUOptionParameter removal
>> > recently?  Otherwise I'm inclined to merge Chunyan's patch - we can
>> > always refactor the code later when QEMUOptionParameter is removed.
>>
>> No, I haven't. I think the QemuOpts conversion series was relatively
>> close, but it seems nobody is working on it any more. Someone should
>> probably pick it up and finish it.
>
>
> Hi, Kevin & Stefan,
> About the nocow option, according to current status, how should we proceed?
> Any
> further work needs to do?

We talked on IRC about rebasing the QEMUOptionParameter series.  Let's
get the in so the 'nocow' change can be made cleanly.

Stefan
diff mbox

Patch

diff --git a/block/cow.c b/block/cow.c
index 909c3e7..13268ba 100644
--- a/block/cow.c
+++ b/block/cow.c
@@ -305,6 +305,7 @@  static int cow_create(const char *filename, QEMUOptionParameter *options,
     Error *local_err = NULL;
     int ret;
     BlockDriverState *cow_bs;
+    int nocow = 0;
 
     /* Read out options */
     while (options && options->name) {
@@ -312,10 +313,26 @@  static int cow_create(const char *filename, QEMUOptionParameter *options,
             image_sectors = options->value.n / 512;
         } else if (!strcmp(options->name, BLOCK_OPT_BACKING_FILE)) {
             image_filename = options->value.s;
+        } else if (!strcmp(options->name, BLOCK_OPT_NOCOW)) {
+            nocow = options->value.n ? 1 : 0;
         }
         options++;
     }
 
+    if (nocow) {
+        QEMUOptionParameter list[] = {
+            {
+                .name = BLOCK_OPT_NOCOW,
+                .type = OPT_FLAG,
+                .value.n = 1,
+                .help = "No copy-on-write",
+                .assigned = true
+            },
+            { NULL }
+        };
+        options = list;
+    }
+
     ret = bdrv_create_file(filename, options, &local_err);
     if (ret < 0) {
         qerror_report_err(local_err);
@@ -375,6 +392,11 @@  static QEMUOptionParameter cow_create_options[] = {
         .type = OPT_STRING,
         .help = "File name of a base image"
     },
+    {
+        .name = BLOCK_OPT_NOCOW,
+        .type = OPT_FLAG,
+        .help = "No copy-on-write"
+    },
     { NULL }
 };
 
diff --git a/block/qcow.c b/block/qcow.c
index c470e05..563ed70 100644
--- a/block/qcow.c
+++ b/block/qcow.c
@@ -671,6 +671,7 @@  static int qcow_create(const char *filename, QEMUOptionParameter *options,
     Error *local_err = NULL;
     int ret;
     BlockDriverState *qcow_bs;
+    int nocow = 0;
 
     /* Read out options */
     while (options && options->name) {
@@ -680,10 +681,26 @@  static int qcow_create(const char *filename, QEMUOptionParameter *options,
             backing_file = options->value.s;
         } else if (!strcmp(options->name, BLOCK_OPT_ENCRYPT)) {
             flags |= options->value.n ? BLOCK_FLAG_ENCRYPT : 0;
+        } else if (!strcmp(options->name, BLOCK_OPT_NOCOW)) {
+            nocow = options->value.n ? 1 : 0;
         }
         options++;
     }
 
+    if (nocow) {
+        QEMUOptionParameter list[] = {
+            {
+                .name = BLOCK_OPT_NOCOW,
+                .type = OPT_FLAG,
+                .value.n = 1,
+                .help = "No copy-on-write",
+                .assigned = true
+            },
+            { NULL }
+        };
+        options = list;
+    }
+
     ret = bdrv_create_file(filename, options, &local_err);
     if (ret < 0) {
         qerror_report_err(local_err);
@@ -895,6 +912,11 @@  static QEMUOptionParameter qcow_create_options[] = {
         .type = OPT_FLAG,
         .help = "Encrypt the image"
     },
+    {
+        .name = BLOCK_OPT_NOCOW,
+        .type = OPT_FLAG,
+        .help = "No copy-on-write"
+    },
     { NULL }
 };
 
diff --git a/block/qcow2.c b/block/qcow2.c
index 6e5d98d..f7ff6c3 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -1612,6 +1612,7 @@  static int qcow2_create(const char *filename, QEMUOptionParameter *options,
     int version = 3;
     Error *local_err = NULL;
     int ret;
+    int nocow = 0;
 
     /* Read out options */
     while (options && options->name) {
@@ -1651,6 +1652,8 @@  static int qcow2_create(const char *filename, QEMUOptionParameter *options,
             }
         } else if (!strcmp(options->name, BLOCK_OPT_LAZY_REFCOUNTS)) {
             flags |= options->value.n ? BLOCK_FLAG_LAZY_REFCOUNTS : 0;
+        } else if (!strcmp(options->name, BLOCK_OPT_NOCOW)) {
+            nocow = options->value.n ? 1 : 0;
         }
         options++;
     }
@@ -1667,6 +1670,20 @@  static int qcow2_create(const char *filename, QEMUOptionParameter *options,
         return -EINVAL;
     }
 
+    if (nocow) {
+        QEMUOptionParameter list[] = {
+            {
+                .name = BLOCK_OPT_NOCOW,
+                .type = OPT_FLAG,
+                .value.n = 1,
+                .help = "No copy-on-write",
+                .assigned = true
+            },
+            { NULL }
+        };
+        options = list;
+    }
+
     ret = qcow2_create2(filename, sectors, backing_file, backing_fmt, flags,
                         cluster_size, prealloc, options, version, &local_err);
     if (error_is_set(&local_err)) {
@@ -2222,6 +2239,11 @@  static QEMUOptionParameter qcow2_create_options[] = {
         .type = OPT_FLAG,
         .help = "Postpone refcount updates",
     },
+    {
+        .name = BLOCK_OPT_NOCOW,
+        .type = OPT_FLAG,
+        .help = "No copy-on-write"
+    },
     { NULL }
 };
 
diff --git a/block/raw-posix.c b/block/raw-posix.c
index f6d48bb..392e3e0 100644
--- a/block/raw-posix.c
+++ b/block/raw-posix.c
@@ -55,6 +55,9 @@ 
 #include <linux/cdrom.h>
 #include <linux/fd.h>
 #include <linux/fs.h>
+#ifndef FS_NOCOW_FL
+#define FS_NOCOW_FL                     0x00800000 /* Do not cow file */
+#endif
 #endif
 #ifdef CONFIG_FIEMAP
 #include <linux/fiemap.h>
@@ -1057,11 +1060,14 @@  static int raw_create(const char *filename, QEMUOptionParameter *options,
     int fd;
     int result = 0;
     int64_t total_size = 0;
+    int nocow = 0;
 
     /* Read out options */
     while (options && options->name) {
         if (!strcmp(options->name, BLOCK_OPT_SIZE)) {
             total_size = options->value.n / BDRV_SECTOR_SIZE;
+        } else if (!strcmp(options->name, BLOCK_OPT_NOCOW)) {
+            nocow = options->value.n ? 1 : 0;
         }
         options++;
     }
@@ -1072,6 +1078,21 @@  static int raw_create(const char *filename, QEMUOptionParameter *options,
         result = -errno;
         error_setg_errno(errp, -result, "Could not create file");
     } else {
+        if (nocow) {
+#ifdef __linux__
+            /* Set NOCOW flag to solve performance issue on fs like btrfs.
+             * This is an optimisation. The FS_IOC_SETFLAGS ioctl return value will
+             * be ignored since any failure of this operation should not block the
+             * left work.
+             */
+            int attr;
+            if (ioctl(fd, FS_IOC_GETFLAGS, &attr) == 0) {
+                attr |= FS_NOCOW_FL;
+                ioctl(fd, FS_IOC_SETFLAGS, &attr);
+            }
+#endif
+        }
+
         if (ftruncate(fd, total_size * BDRV_SECTOR_SIZE) != 0) {
             result = -errno;
             error_setg_errno(errp, -result, "Could not resize file");
@@ -1206,6 +1227,11 @@  static QEMUOptionParameter raw_create_options[] = {
         .type = OPT_SIZE,
         .help = "Virtual disk size"
     },
+    {
+        .name = BLOCK_OPT_NOCOW,
+        .type = OPT_FLAG,
+        .help = "No copy-on-write"
+    },
     { NULL }
 };
 
diff --git a/block/vdi.c b/block/vdi.c
index b6ec002..a569b19 100644
--- a/block/vdi.c
+++ b/block/vdi.c
@@ -53,6 +53,13 @@ 
 #include "block/block_int.h"
 #include "qemu/module.h"
 #include "migration/migration.h"
+#ifdef __linux__
+#include <linux/fs.h>
+#include <sys/ioctl.h>
+#ifndef FS_NOCOW_FL
+#define FS_NOCOW_FL                     0x00800000 /* Do not cow file */
+#endif
+#endif
 
 #if defined(CONFIG_UUID)
 #include <uuid/uuid.h>
@@ -657,6 +664,7 @@  static int vdi_create(const char *filename, QEMUOptionParameter *options,
     VdiHeader header;
     size_t i;
     size_t bmap_size;
+    int nocow = 0;
 
     logout("\n");
 
@@ -677,6 +685,8 @@  static int vdi_create(const char *filename, QEMUOptionParameter *options,
                 image_type = VDI_TYPE_STATIC;
             }
 #endif
+        } else if (!strcmp(options->name, BLOCK_OPT_NOCOW)) {
+            nocow = options->value.n ? 1 : 0;
         }
         options++;
     }
@@ -688,6 +698,21 @@  static int vdi_create(const char *filename, QEMUOptionParameter *options,
         return -errno;
     }
 
+    if (nocow) {
+#ifdef __linux__
+        /* Set NOCOW flag to solve performance issue on fs like btrfs.
+         * This is an optimisation. The FS_IOC_SETFLAGS ioctl return value will
+         * be ignored since any failure of this operation should not block the
+         * left work.
+         */
+        int attr;
+        if (ioctl(fd, FS_IOC_GETFLAGS, &attr) == 0) {
+            attr |= FS_NOCOW_FL;
+            ioctl(fd, FS_IOC_SETFLAGS, &attr);
+        }
+#endif
+    }
+
     /* We need enough blocks to store the given disk size,
        so always round up. */
     blocks = (bytes + block_size - 1) / block_size;
@@ -780,6 +805,11 @@  static QEMUOptionParameter vdi_create_options[] = {
         .help = "VDI static (pre-allocated) image"
     },
 #endif
+    {
+        .name = BLOCK_OPT_NOCOW,
+        .type = OPT_FLAG,
+        .help = "No copy-on-write"
+    },
     /* TODO: An additional option to set UUID values might be useful. */
     { NULL }
 };
diff --git a/block/vmdk.c b/block/vmdk.c
index a7ebd0f..d662dec 100644
--- a/block/vmdk.c
+++ b/block/vmdk.c
@@ -28,6 +28,13 @@ 
 #include "qemu/module.h"
 #include "migration/migration.h"
 #include <zlib.h>
+#ifdef __linux__
+#include <linux/fs.h>
+#include <sys/ioctl.h>
+#ifndef FS_NOCOW_FL
+#define FS_NOCOW_FL                     0x00800000 /* Do not cow file */
+#endif
+#endif
 
 #define VMDK3_MAGIC (('C' << 24) | ('O' << 16) | ('W' << 8) | 'D')
 #define VMDK4_MAGIC (('K' << 24) | ('D' << 16) | ('M' << 8) | 'V')
@@ -1435,7 +1442,8 @@  static int coroutine_fn vmdk_co_write_zeroes(BlockDriverState *bs,
 }
 
 static int vmdk_create_extent(const char *filename, int64_t filesize,
-                              bool flat, bool compress, bool zeroed_grain)
+                              bool flat, bool compress, bool zeroed_grain,
+                              int nocow)
 {
     int ret, i;
     int fd = 0;
@@ -1447,7 +1455,23 @@  static int vmdk_create_extent(const char *filename, int64_t filesize,
                    0644);
     if (fd < 0) {
         return -errno;
+    } 
+    
+    if (nocow) {
+#ifdef __linux__
+        /* Set NOCOW flag to solve performance issue on fs like btrfs.
+         * This is an optimisation. The FS_IOC_SETFLAGS ioctl return value will
+         * be ignored since any failure of this operation should not block the
+         * left work.
+         */
+        int attr;
+        if (ioctl(fd, FS_IOC_GETFLAGS, &attr) == 0) {
+            attr |= FS_NOCOW_FL;
+            ioctl(fd, FS_IOC_SETFLAGS, &attr);
+        }
+#endif
     }
+
     if (flat) {
         ret = ftruncate(fd, filesize);
         if (ret < 0) {
@@ -1604,6 +1628,7 @@  static int vmdk_create(const char *filename, QEMUOptionParameter *options,
     uint32_t parent_cid = 0xffffffff;
     uint32_t number_heads = 16;
     bool zeroed_grain = false;
+    int nocow = 0;
     const char desc_template[] =
         "# Disk DescriptorFile\n"
         "version=1\n"
@@ -1641,6 +1666,8 @@  static int vmdk_create(const char *filename, QEMUOptionParameter *options,
             fmt = options->value.s;
         } else if (!strcmp(options->name, BLOCK_OPT_ZEROED_GRAIN)) {
             zeroed_grain |= options->value.n;
+        } else if (!strcmp(options->name, BLOCK_OPT_NOCOW)) {
+            nocow = options->value.n ? 1 : 0;
         }
         options++;
     }
@@ -1729,7 +1756,7 @@  static int vmdk_create(const char *filename, QEMUOptionParameter *options,
                 path, desc_filename);
 
         if (vmdk_create_extent(ext_filename, size,
-                               flat, compress, zeroed_grain)) {
+                               flat, compress, zeroed_grain, nocow)) {
             return -EINVAL;
         }
         filesize -= size;
@@ -1926,6 +1953,11 @@  static QEMUOptionParameter vmdk_create_options[] = {
         .type = OPT_FLAG,
         .help = "Enable efficient zero writes using the zeroed-grain GTE feature"
     },
+    {
+        .name = BLOCK_OPT_NOCOW,
+        .type = OPT_FLAG,
+        .help = "No copy-on-write"
+    },
     { NULL }
 };
 
diff --git a/include/block/block_int.h b/include/block/block_int.h
index 1666066..f03568d 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -53,6 +53,7 @@ 
 #define BLOCK_OPT_COMPAT_LEVEL      "compat"
 #define BLOCK_OPT_LAZY_REFCOUNTS    "lazy_refcounts"
 #define BLOCK_OPT_ADAPTER_TYPE      "adapter_type"
+#define BLOCK_OPT_NOCOW             "nocow"
 
 typedef struct BdrvTrackedRequest {
     BlockDriverState *bs;