Commit 73c632edc4e28d0a8cae3b34abf7ae46fa33bf1e
Committed by
Anthony Liguori
1 parent
efa84d43
qcow2: Allow different cluster sizes
Add an option to specify the cluster size of a newly created qcow2 image. Default is 4k which is the same value that was hard-coded before. Signed-off-by: Kevin Wolf <kwolf@redhat.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
Showing
2 changed files
with
49 additions
and
5 deletions
block/qcow2.c
| ... | ... | @@ -62,6 +62,9 @@ |
| 62 | 62 | |
| 63 | 63 | #define REFCOUNT_SHIFT 1 /* refcount size is 2 bytes */ |
| 64 | 64 | |
| 65 | +#define MIN_CLUSTER_BITS 9 | |
| 66 | +#define MAX_CLUSTER_BITS 16 | |
| 67 | + | |
| 65 | 68 | typedef struct QCowHeader { |
| 66 | 69 | uint32_t magic; |
| 67 | 70 | uint32_t version; |
| ... | ... | @@ -300,8 +303,8 @@ static int qcow_open(BlockDriverState *bs, const char *filename, int flags) |
| 300 | 303 | if (header.magic != QCOW_MAGIC || header.version != QCOW_VERSION) |
| 301 | 304 | goto fail; |
| 302 | 305 | if (header.size <= 1 || |
| 303 | - header.cluster_bits < 9 || | |
| 304 | - header.cluster_bits > 16) | |
| 306 | + header.cluster_bits < MIN_CLUSTER_BITS || | |
| 307 | + header.cluster_bits > MAX_CLUSTER_BITS) | |
| 305 | 308 | goto fail; |
| 306 | 309 | if (header.crypt_method > QCOW_CRYPT_AES) |
| 307 | 310 | goto fail; |
| ... | ... | @@ -1583,9 +1586,30 @@ static void create_refcount_update(QCowCreateState *s, |
| 1583 | 1586 | } |
| 1584 | 1587 | } |
| 1585 | 1588 | |
| 1589 | +static int get_bits_from_size(size_t size) | |
| 1590 | +{ | |
| 1591 | + int res = 0; | |
| 1592 | + | |
| 1593 | + if (size == 0) { | |
| 1594 | + return -1; | |
| 1595 | + } | |
| 1596 | + | |
| 1597 | + while (size != 1) { | |
| 1598 | + /* Not a power of two */ | |
| 1599 | + if (size & 1) { | |
| 1600 | + return -1; | |
| 1601 | + } | |
| 1602 | + | |
| 1603 | + size >>= 1; | |
| 1604 | + res++; | |
| 1605 | + } | |
| 1606 | + | |
| 1607 | + return res; | |
| 1608 | +} | |
| 1609 | + | |
| 1586 | 1610 | static int qcow_create2(const char *filename, int64_t total_size, |
| 1587 | 1611 | const char *backing_file, const char *backing_format, |
| 1588 | - int flags) | |
| 1612 | + int flags, size_t cluster_size) | |
| 1589 | 1613 | { |
| 1590 | 1614 | |
| 1591 | 1615 | int fd, header_size, backing_filename_len, l1_size, i, shift, l2_bits; |
| ... | ... | @@ -1619,8 +1643,20 @@ static int qcow_create2(const char *filename, int64_t total_size, |
| 1619 | 1643 | header.backing_file_size = cpu_to_be32(backing_filename_len); |
| 1620 | 1644 | header_size += backing_filename_len; |
| 1621 | 1645 | } |
| 1622 | - s->cluster_bits = 12; /* 4 KB clusters */ | |
| 1646 | + | |
| 1647 | + /* Cluster size */ | |
| 1648 | + s->cluster_bits = get_bits_from_size(cluster_size); | |
| 1649 | + if (s->cluster_bits < MIN_CLUSTER_BITS || | |
| 1650 | + s->cluster_bits > MAX_CLUSTER_BITS) | |
| 1651 | + { | |
| 1652 | + fprintf(stderr, "Cluster size must be a power of two between " | |
| 1653 | + "%d and %dk\n", | |
| 1654 | + 1 << MIN_CLUSTER_BITS, | |
| 1655 | + 1 << (MAX_CLUSTER_BITS - 10)); | |
| 1656 | + return -EINVAL; | |
| 1657 | + } | |
| 1623 | 1658 | s->cluster_size = 1 << s->cluster_bits; |
| 1659 | + | |
| 1624 | 1660 | header.cluster_bits = cpu_to_be32(s->cluster_bits); |
| 1625 | 1661 | header_size = (header_size + 7) & ~7; |
| 1626 | 1662 | if (flags & BLOCK_FLAG_ENCRYPT) { |
| ... | ... | @@ -1702,6 +1738,7 @@ static int qcow_create(const char *filename, QEMUOptionParameter *options) |
| 1702 | 1738 | const char *backing_fmt = NULL; |
| 1703 | 1739 | uint64_t sectors = 0; |
| 1704 | 1740 | int flags = 0; |
| 1741 | + size_t cluster_size = 4096; | |
| 1705 | 1742 | |
| 1706 | 1743 | /* Read out options */ |
| 1707 | 1744 | while (options && options->name) { |
| ... | ... | @@ -1713,11 +1750,16 @@ static int qcow_create(const char *filename, QEMUOptionParameter *options) |
| 1713 | 1750 | backing_fmt = options->value.s; |
| 1714 | 1751 | } else if (!strcmp(options->name, BLOCK_OPT_ENCRYPT)) { |
| 1715 | 1752 | flags |= options->value.n ? BLOCK_FLAG_ENCRYPT : 0; |
| 1753 | + } else if (!strcmp(options->name, BLOCK_OPT_CLUSTER_SIZE)) { | |
| 1754 | + if (options->value.n) { | |
| 1755 | + cluster_size = options->value.n; | |
| 1756 | + } | |
| 1716 | 1757 | } |
| 1717 | 1758 | options++; |
| 1718 | 1759 | } |
| 1719 | 1760 | |
| 1720 | - return qcow_create2(filename, sectors, backing_file, backing_fmt, flags); | |
| 1761 | + return qcow_create2(filename, sectors, backing_file, backing_fmt, flags, | |
| 1762 | + cluster_size); | |
| 1721 | 1763 | } |
| 1722 | 1764 | |
| 1723 | 1765 | static int qcow_make_empty(BlockDriverState *bs) |
| ... | ... | @@ -2920,6 +2962,7 @@ static QEMUOptionParameter qcow_create_options[] = { |
| 2920 | 2962 | { BLOCK_OPT_BACKING_FILE, OPT_STRING }, |
| 2921 | 2963 | { BLOCK_OPT_BACKING_FMT, OPT_STRING }, |
| 2922 | 2964 | { BLOCK_OPT_ENCRYPT, OPT_FLAG }, |
| 2965 | + { BLOCK_OPT_CLUSTER_SIZE, OPT_SIZE }, | |
| 2923 | 2966 | { NULL } |
| 2924 | 2967 | }; |
| 2925 | 2968 | ... | ... |
block_int.h
| ... | ... | @@ -36,6 +36,7 @@ |
| 36 | 36 | #define BLOCK_OPT_COMPAT6 "compat6" |
| 37 | 37 | #define BLOCK_OPT_BACKING_FILE "backing_file" |
| 38 | 38 | #define BLOCK_OPT_BACKING_FMT "backing_fmt" |
| 39 | +#define BLOCK_OPT_CLUSTER_SIZE "cluster_size" | |
| 39 | 40 | |
| 40 | 41 | typedef struct AIOPool { |
| 41 | 42 | void (*cancel)(BlockDriverAIOCB *acb); | ... | ... |