This patch is part of the Fast Virtual Disk (FVD) proposal. See http://wiki.qemu.org/Features/FVD.
This patch adds FVD's implementation of the bdrv_update() interface. Signed-off-by: Chunqiang Tang <ct...@us.ibm.com> --- block/fvd-update.c | 274 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 272 insertions(+), 2 deletions(-) diff --git a/block/fvd-update.c b/block/fvd-update.c index 2498618..4ef4969 100644 --- a/block/fvd-update.c +++ b/block/fvd-update.c @@ -1,5 +1,5 @@ /* - * QEMU Fast Virtual Disk Format bdrv_update + * QEMU Fast Virtual Disk Format Misc Functions of BlockDriver Interface * * Copyright IBM, Corp. 2010 * @@ -13,9 +13,279 @@ static int fvd_update(BlockDriverState * bs, QEMUOptionParameter * options) { - return -ENOTSUP; + BDRVFvdState *s = bs->opaque; + FvdHeader header; + int ret; + + read_fvd_header(s, &header); + + while (options && options->name) { + if (!strcmp(options->name, BLOCK_OPT_SIZE)) { + if (header.table_offset > 0) { + fprintf(stderr, "Cannot resize a compact FVD image.\n"); + return -EINVAL; + } + if (options->value.n < header.virtual_disk_size) { + printf("Warning: image's new size %" PRId64 + " is smaller than the original size %" PRId64 + ". Some image data will be truncated.\n", + options->value.n, header.virtual_disk_size); + } + header.virtual_disk_size = options->value.n; + printf("Image resized to %" PRId64 " bytes.\n", options->value.n); + } else if (!strcmp(options->name, BLOCK_OPT_BACKING_FILE)) { + if (strlen(options->value.s) > 1023) { + fprintf(stderr, "Error: the new base image name is longer " + "than 1023, which is not allowed.\n"); + return -EINVAL; + } + memset(header.base_img, 0, 1024); + pstrcpy(header.base_img, 1024, options->value.s); + printf("Backing file updated to '%s'.\n", options->value.s); + } else if (!strcmp(options->name, "data_file")) { + if (strlen(options->value.s) > 1023) { + fprintf(stderr, "Error: the new data file name is longer " + "than 1023, which is not allowed.\n"); + return -EINVAL; + } + + memset(header.data_file, 0, 1024); + pstrcpy(header.data_file, 1024, options->value.s); + printf("Data file updated to '%s'.\n", options->value.s); + } else if (!strcmp(options->name, "need_zero_init")) { + header.need_zero_init = options->value.n; + if (header.need_zero_init) { + printf("need_zero_init is turned on.\n"); + } else { + printf("need_zero_init is turned off.\n"); + } + } else if (!strcmp(options->name, "copy_on_read")) { + header.copy_on_read = options->value.n; + if (header.copy_on_read) { + printf("Copy on read is enabled for this disk.\n"); + } else { + printf("Copy on read is disabled for this disk.\n"); + } + } else if (!strcmp(options->name, "clean_shutdown")) { + header.clean_shutdown = options->value.n; + if (header.clean_shutdown) { + printf("clean_shutdown is manually set to true\n"); + } else { + printf("clean_shutdown is manually set to false\n"); + } + } else if (!strcmp(options->name, "journal_buf_size")) { + header.journal_buf_size = options->value.n; + printf("journal_buf_size is updated to %"PRIu64" bytes.\n", + header.journal_buf_size); + } else if (!strcmp(options->name, "journal_clean_buf_period")) { + header.journal_clean_buf_period = options->value.n; + printf("journal_clean_buf_period is updated to %"PRIu64 + " milliseconds.\n", + header.journal_clean_buf_period); + } else if (!strcmp(options->name,"max_outstanding_copy_on_read_data")) { + header.max_outstanding_copy_on_read_data = options->value.n; + if (header.max_outstanding_copy_on_read_data <= 0) { + fprintf(stderr, "Error: max_outstanding_copy_on_read_data " + "must be positive.\n"); + return -EINVAL; + } + printf("max_outstanding_copy_on_read_data updated to %" PRId64 + ".\n", header.max_outstanding_copy_on_read_data); + } else if (!strcmp(options->name, "init_data_region")) { + if (options->value.n && !s->data_region_prepared) { + init_data_region(s); + } + } else if (!strcmp(options->name, "prefetch_start_delay")) { + if (options->value.n <= 0) { + header.prefetch_start_delay = -1; + } else { + header.prefetch_start_delay = options->value.n; + } + if (header.prefetch_start_delay > 0) { + printf("Prefetch starting delay updated to %" PRId64 + " seconds.\n", header.prefetch_start_delay); + } else { + printf("Prefetch starting delay updated to %" PRId64 + " seconds. " + "Because of the negative value, prefetching is " + "disabled for this image.\n", + header.prefetch_start_delay); + } + } else if (!strcmp(options->name, "num_prefetch_slots")) { + header.num_prefetch_slots = options->value.n; + if (header.num_prefetch_slots < 1) { + fprintf(stderr, "Error: num_prefetch_slots " + "%d is not a positive integer.\n", + header.num_prefetch_slots); + return -EINVAL; + } + printf("num_prefetch_slots updated to %d.\n", + header.num_prefetch_slots); + } else if (!strcmp(options->name, "bytes_per_prefetch")) { + header.bytes_per_prefetch = options->value.n; + if (header.bytes_per_prefetch < DEF_PAGE_SIZE) { + fprintf(stderr, "Error: bytes_per_prefetch cannot be smaller " + "than %d.\n", DEF_PAGE_SIZE); + return -EINVAL; + } + printf("bytes_per_prefetch updated to %" PRIu64 ".\n", + header.bytes_per_prefetch); + } else if (!strcmp(options->name, "prefetch_min_read_throughput")) { + header.prefetch_min_read_throughput = options->value.n; + printf("prefetch_min_read_throughput updated to %" + PRIu64 " KB/s\n", header.prefetch_min_read_throughput); + } else if (!strcmp(options->name, "prefetch_min_write_throughput")) { + header.prefetch_min_write_throughput = options->value.n; + printf("prefetch_min_write_throughput updated to %" + PRIu64 "KB/s\n", header.prefetch_min_write_throughput); + } else if (!strcmp(options->name, + "prefetch_read_throughput_measure_time")) { + header.prefetch_read_throughput_measure_time = options->value.n; + printf("prefetch_read_throughput_measure_time updated to %" PRIu64 + " ms\n", header.prefetch_read_throughput_measure_time); + } else if (!strcmp(options->name, + "prefetch_write_throughput_measure_time")) { + header.prefetch_write_throughput_measure_time = options->value.n; + printf("prefetch_write_throughput_measure_time updated to %" PRIu64 + " ms\n", header.prefetch_write_throughput_measure_time); + } else if (!strcmp(options->name, + "prefetch_over_threshold_throttle_time")) { + header.prefetch_throttle_time = options->value.n; + if (header.prefetch_throttle_time > 0) { + printf("prefetch_over_threshold_throttle_time updated to %" + PRIu64 "ms.\n", header.prefetch_throttle_time); + } else { + printf("prefetch_over_threshold_throttle_time updated to %" + PRIu64 "ms. It is not positive and hence no " + "throttling will be applied to prefetch.\n", + header.prefetch_throttle_time); + } + } else if (!strcmp(options->name, "storage_grow_unit")) { + header.storage_grow_unit = options->value.n; + if (header.storage_grow_unit < header.chunk_size) { + header.storage_grow_unit = header.chunk_size; + } + printf("storage_grow_unit updated to %" PRIu64 "\n", + header.storage_grow_unit); + } else if (!strcmp(options->name, "add_storage_cmd")) { + if (strlen(options->value.s) > 1023) { + fprintf(stderr, "Error: add_storage_cmd is longer than 1023, " + "which is not allowed.\n"); + return -EINVAL; + } + pstrcpy(header.add_storage_cmd, 1024, options->value.s); + } else { + fprintf(stderr, "Error: unknown option '%s=%s'\n", + options->name, options->value.s); + return -EINVAL; + } + options++; + } + + if ((ret = update_fvd_header(s, &header))) { + return ret; + } + ret = bdrv_flush(s->fvd_metadata); + return ret; } static QEMUOptionParameter fvd_update_options[] = { + { + .name = BLOCK_OPT_SIZE, + .type = OPT_SIZE, + .help = "Virtual disk size"}, + { + .name = "storage_grow_unit", + .type = OPT_SIZE, + .help = "Storage grow unit"}, + { + .name = "add_storage_cmd", + .type = OPT_STRING, + .help = "Command to add storage when running out of space"}, + { + .name = BLOCK_OPT_BACKING_FILE, + .type = OPT_STRING, + .help = "File name of a backing image"}, + { + .name = BLOCK_OPT_BACKING_FMT, + .type = OPT_STRING, + .help = "Image format of the backing image"}, + { + .name = "data_file", + .type = OPT_STRING, + .help = "File name of a data file"}, + { + .name = "data_file_fmt", + .type = OPT_STRING, + .help = "Image format of the data file"}, + { + .name = "copy_on_read", + .type = OPT_FLAG, + .help = "copy_on_read=on|off"}, + { + .name = "prefetch_start_delay", + .type = OPT_NUMBER, + .help = "Delay in seconds before starting whole image prefetching. "}, + { + .name = "journal_size", + .type = OPT_SIZE, + .help = "Journal size"}, + { + .name = "need_zero_init", + .type = OPT_FLAG, + .help = "compact_image=on|off"}, + { + .name = "max_outstanding_copy_on_read_data", + .type = OPT_SIZE, + .help = "copy_on_read is temporarily disabled when unsaved data exceed " + "this threshold (in bytes)"}, + { + .name = "init_data_region", + .type = OPT_FLAG, + .help = "if enabled the image file will be expanded to its full size"}, + { + .name = "journal_buf_size", + .type = OPT_SIZE, + .help = "size of in-memory journal buffer (in bytes)"}, + { + .name = "journal_clean_buf_period", + .type = OPT_NUMBER, + .help = "(milliseconds)"}, + { + .name = "num_prefetch_slots", + .type = OPT_NUMBER, + .help = "Number of concurrent prefetches allowed"}, + { + .name = "bytes_per_prefetch", + .type = OPT_NUMBER, + .help = "Data to read per prefetch"}, + { + .name = "prefetch_over_threshold_throttle_time", + .type = OPT_NUMBER, + .help = "(in milliseconds)"}, + { + .name = "prefetch_read_throughput_measure_time", + .type = OPT_NUMBER, + .help = "(in milliseconds)"}, + { + .name = "prefetch_write_throughput_measure_time", + .type = OPT_NUMBER, + .help = "(in milliseconds)"}, + { + .name = "prefetch_min_read_throughput", + .type = OPT_NUMBER, + .help = "(in KB/s)"}, + { + .name = "prefetch_max_read_throughput", + .type = OPT_NUMBER, + .help = "(in KB/s)"}, + { + .name = "prefetch_min_write_throughput", + .type = OPT_NUMBER, + .help = "(in KB/s)"}, + { + .name = "prefetch_max_write_throughput", + .type = OPT_NUMBER, + .help = "(in KB/s)"}, {NULL} }; -- 1.7.0.4