qemu-img: Require larger zero areas for sparse handling
By default, require 4k of consecutive zero bytes for qemu-img to make the output file sparse by not issuing a write request for the zeroed parts. Add an -S option to allow users to tune this setting. This helps to avoid situations where a lot of zero sectors and data sectors are mixed and qemu-img tended to issue many tiny 512 byte writes. Signed-off-by: Kevin Wolf <kwolf@redhat.com>
This commit is contained in:
parent
c488c7f649
commit
a22f123ca3
@ -28,9 +28,9 @@ STEXI
|
|||||||
ETEXI
|
ETEXI
|
||||||
|
|
||||||
DEF("convert", img_convert,
|
DEF("convert", img_convert,
|
||||||
"convert [-c] [-p] [-f fmt] [-t cache] [-O output_fmt] [-o options] [-s snapshot_name] filename [filename2 [...]] output_filename")
|
"convert [-c] [-p] [-f fmt] [-t cache] [-O output_fmt] [-o options] [-s snapshot_name] [-S sparse_size] filename [filename2 [...]] output_filename")
|
||||||
STEXI
|
STEXI
|
||||||
@item convert [-c] [-p] [-f @var{fmt}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_name}] @var{filename} [@var{filename2} [...]] @var{output_filename}
|
@item convert [-c] [-p] [-f @var{fmt}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_name}] [-S @var{sparse_size}] @var{filename} [@var{filename2} [...]] @var{output_filename}
|
||||||
ETEXI
|
ETEXI
|
||||||
|
|
||||||
DEF("info", img_info,
|
DEF("info", img_info,
|
||||||
|
61
qemu-img.c
61
qemu-img.c
@ -82,6 +82,8 @@ static void help(void)
|
|||||||
" rebasing in this case (useful for renaming the backing file)\n"
|
" rebasing in this case (useful for renaming the backing file)\n"
|
||||||
" '-h' with or without a command shows this help and lists the supported formats\n"
|
" '-h' with or without a command shows this help and lists the supported formats\n"
|
||||||
" '-p' show progress of command (only certain commands)\n"
|
" '-p' show progress of command (only certain commands)\n"
|
||||||
|
" '-S' indicates the consecutive number of bytes that must contain only zeros\n"
|
||||||
|
" for qemu-img to create a sparse image during conversion\n"
|
||||||
"\n"
|
"\n"
|
||||||
"Parameters to snapshot subcommand:\n"
|
"Parameters to snapshot subcommand:\n"
|
||||||
" 'snapshot' is the name of the snapshot to create, apply or delete\n"
|
" 'snapshot' is the name of the snapshot to create, apply or delete\n"
|
||||||
@ -570,6 +572,48 @@ static int is_allocated_sectors(const uint8_t *buf, int n, int *pnum)
|
|||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Like is_allocated_sectors, but if the buffer starts with a used sector,
|
||||||
|
* up to 'min' consecutive sectors containing zeros are ignored. This avoids
|
||||||
|
* breaking up write requests for only small sparse areas.
|
||||||
|
*/
|
||||||
|
static int is_allocated_sectors_min(const uint8_t *buf, int n, int *pnum,
|
||||||
|
int min)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
int num_checked, num_used;
|
||||||
|
|
||||||
|
if (n < min) {
|
||||||
|
min = n;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = is_allocated_sectors(buf, n, pnum);
|
||||||
|
if (!ret) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
num_used = *pnum;
|
||||||
|
buf += BDRV_SECTOR_SIZE * *pnum;
|
||||||
|
n -= *pnum;
|
||||||
|
num_checked = num_used;
|
||||||
|
|
||||||
|
while (n > 0) {
|
||||||
|
ret = is_allocated_sectors(buf, n, pnum);
|
||||||
|
|
||||||
|
buf += BDRV_SECTOR_SIZE * *pnum;
|
||||||
|
n -= *pnum;
|
||||||
|
num_checked += *pnum;
|
||||||
|
if (ret) {
|
||||||
|
num_used = num_checked;
|
||||||
|
} else if (*pnum >= min) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*pnum = num_used;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Compares two buffers sector by sector. Returns 0 if the first sector of both
|
* Compares two buffers sector by sector. Returns 0 if the first sector of both
|
||||||
* buffers matches, non-zero otherwise.
|
* buffers matches, non-zero otherwise.
|
||||||
@ -620,6 +664,7 @@ static int img_convert(int argc, char **argv)
|
|||||||
char *options = NULL;
|
char *options = NULL;
|
||||||
const char *snapshot_name = NULL;
|
const char *snapshot_name = NULL;
|
||||||
float local_progress;
|
float local_progress;
|
||||||
|
int min_sparse = 8; /* Need at least 4k of zeros for sparse detection */
|
||||||
|
|
||||||
fmt = NULL;
|
fmt = NULL;
|
||||||
out_fmt = "raw";
|
out_fmt = "raw";
|
||||||
@ -627,7 +672,7 @@ static int img_convert(int argc, char **argv)
|
|||||||
out_baseimg = NULL;
|
out_baseimg = NULL;
|
||||||
compress = 0;
|
compress = 0;
|
||||||
for(;;) {
|
for(;;) {
|
||||||
c = getopt(argc, argv, "f:O:B:s:hce6o:pt:");
|
c = getopt(argc, argv, "f:O:B:s:hce6o:pS:t:");
|
||||||
if (c == -1) {
|
if (c == -1) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -662,6 +707,18 @@ static int img_convert(int argc, char **argv)
|
|||||||
case 's':
|
case 's':
|
||||||
snapshot_name = optarg;
|
snapshot_name = optarg;
|
||||||
break;
|
break;
|
||||||
|
case 'S':
|
||||||
|
{
|
||||||
|
int64_t sval;
|
||||||
|
sval = strtosz_suffix(optarg, NULL, STRTOSZ_DEFSUFFIX_B);
|
||||||
|
if (sval < 0) {
|
||||||
|
error_report("Invalid minimum zero buffer size for sparse output specified");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
min_sparse = sval / BDRV_SECTOR_SIZE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
case 'p':
|
case 'p':
|
||||||
progress = 1;
|
progress = 1;
|
||||||
break;
|
break;
|
||||||
@ -970,7 +1027,7 @@ static int img_convert(int argc, char **argv)
|
|||||||
sectors that are entirely 0, since whatever data was
|
sectors that are entirely 0, since whatever data was
|
||||||
already there is garbage, not 0s. */
|
already there is garbage, not 0s. */
|
||||||
if (!has_zero_init || out_baseimg ||
|
if (!has_zero_init || out_baseimg ||
|
||||||
is_allocated_sectors(buf1, n, &n1)) {
|
is_allocated_sectors_min(buf1, n, &n1, min_sparse)) {
|
||||||
ret = bdrv_write(out_bs, sector_num, buf1, n1);
|
ret = bdrv_write(out_bs, sector_num, buf1, n1);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_report("error while writing sector %" PRId64
|
error_report("error while writing sector %" PRId64
|
||||||
|
@ -40,6 +40,11 @@ indicates that target image must be compressed (qcow format only)
|
|||||||
with or without a command shows help and lists the supported formats
|
with or without a command shows help and lists the supported formats
|
||||||
@item -p
|
@item -p
|
||||||
display progress bar (convert and rebase commands only)
|
display progress bar (convert and rebase commands only)
|
||||||
|
@item -S @var{size}
|
||||||
|
indicates the consecutive number of bytes that must contain only zeros
|
||||||
|
for qemu-img to create a sparse image during conversion. This value is rounded
|
||||||
|
down to the nearest 512 bytes. You may use the common size suffixes like
|
||||||
|
@code{k} for kilobytes.
|
||||||
@end table
|
@end table
|
||||||
|
|
||||||
Parameters to snapshot subcommand:
|
Parameters to snapshot subcommand:
|
||||||
@ -86,7 +91,7 @@ it doesn't need to be specified separately in this case.
|
|||||||
|
|
||||||
Commit the changes recorded in @var{filename} in its base image.
|
Commit the changes recorded in @var{filename} in its base image.
|
||||||
|
|
||||||
@item convert [-c] [-p] [-f @var{fmt}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_name}] @var{filename} [@var{filename2} [...]] @var{output_filename}
|
@item convert [-c] [-p] [-f @var{fmt}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_name}] [-S @var{sparse_size}] @var{filename} [@var{filename2} [...]] @var{output_filename}
|
||||||
|
|
||||||
Convert the disk image @var{filename} or a snapshot @var{snapshot_name} to disk image @var{output_filename}
|
Convert the disk image @var{filename} or a snapshot @var{snapshot_name} to disk image @var{output_filename}
|
||||||
using format @var{output_fmt}. It can be optionally compressed (@code{-c}
|
using format @var{output_fmt}. It can be optionally compressed (@code{-c}
|
||||||
|
Loading…
Reference in New Issue
Block a user