From 8ddd08c5d1415a71f21157686d43f48ff14992b6 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Tue, 28 May 2013 11:19:51 +0200 Subject: qemu-iotests: fix 054 cluster size help output Commit f3f4d2c09b9cf46903ba38425ec46c44185162bd added a hint to increase the cluster size when a large image cannot be created. Test 054 now has outdated output and fails because the golden output does not match. This patch updates the 054 golden output. Signed-off-by: Stefan Hajnoczi Signed-off-by: Kevin Wolf --- tests/qemu-iotests/054.out | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/qemu-iotests/054.out b/tests/qemu-iotests/054.out index 0b2fe301ea..2f357c271d 100644 --- a/tests/qemu-iotests/054.out +++ b/tests/qemu-iotests/054.out @@ -1,7 +1,7 @@ QA output created by 054 creating too large image (1 EB) -qemu-img: The image size is too large for file format 'qcow2' +qemu-img: The image size is too large for file format 'qcow2' (try using a larger cluster size) Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1152921504606846976 creating too large image (1 EB) using qcow2.py -- cgit v1.2.1 From b64ec4e4ade581d662753cdeb0d7e0e27aafbf81 Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Wed, 29 May 2013 19:35:40 +0800 Subject: block: add block driver read only whitelist We may want to include a driver in the whitelist for read only tasks such as diagnosing or exporting guest data (with libguestfs as a good example). This patch introduces a readonly whitelist option, and for backward compatibility, the old configure option --block-drv-whitelist is now an alias to rw whitelist. Drivers in readonly list is only permitted to open file readonly, and returns -ENOTSUP for RW opening. E.g. To include vmdk readonly, and others read+write: ./configure --target-list=x86_64-softmmu \ --block-drv-rw-whitelist=qcow2,raw,file,qed \ --block-drv-ro-whitelist=vmdk Signed-off-by: Fam Zheng Signed-off-by: Kevin Wolf --- block.c | 43 +++++++++++++++++++++++++++---------------- blockdev.c | 4 ++-- configure | 20 +++++++++++++++----- hw/block/xen_disk.c | 8 ++++++-- include/block/block.h | 3 ++- scripts/create_config | 11 +++++++++-- 6 files changed, 61 insertions(+), 28 deletions(-) diff --git a/block.c b/block.c index 3f87489937..65c0b60736 100644 --- a/block.c +++ b/block.c @@ -328,28 +328,40 @@ BlockDriver *bdrv_find_format(const char *format_name) return NULL; } -static int bdrv_is_whitelisted(BlockDriver *drv) +static int bdrv_is_whitelisted(BlockDriver *drv, bool read_only) { - static const char *whitelist[] = { - CONFIG_BDRV_WHITELIST + static const char *whitelist_rw[] = { + CONFIG_BDRV_RW_WHITELIST + }; + static const char *whitelist_ro[] = { + CONFIG_BDRV_RO_WHITELIST }; const char **p; - if (!whitelist[0]) + if (!whitelist_rw[0] && !whitelist_ro[0]) { return 1; /* no whitelist, anything goes */ + } - for (p = whitelist; *p; p++) { + for (p = whitelist_rw; *p; p++) { if (!strcmp(drv->format_name, *p)) { return 1; } } + if (read_only) { + for (p = whitelist_ro; *p; p++) { + if (!strcmp(drv->format_name, *p)) { + return 1; + } + } + } return 0; } -BlockDriver *bdrv_find_whitelisted_format(const char *format_name) +BlockDriver *bdrv_find_whitelisted_format(const char *format_name, + bool read_only) { BlockDriver *drv = bdrv_find_format(format_name); - return drv && bdrv_is_whitelisted(drv) ? drv : NULL; + return drv && bdrv_is_whitelisted(drv, read_only) ? drv : NULL; } typedef struct CreateCo { @@ -684,10 +696,6 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file, trace_bdrv_open_common(bs, filename ?: "", flags, drv->format_name); - if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv)) { - return -ENOTSUP; - } - /* bdrv_open() with directly using a protocol as drv. This layer is already * opened, so assign it to bs (while file becomes a closed BlockDriverState) * and return immediately. */ @@ -698,9 +706,15 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file, bs->open_flags = flags; bs->buffer_alignment = 512; + open_flags = bdrv_open_flags(bs, flags); + bs->read_only = !(open_flags & BDRV_O_RDWR); + + if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv, bs->read_only)) { + return -ENOTSUP; + } assert(bs->copy_on_read == 0); /* bdrv_new() and bdrv_close() make it so */ - if ((flags & BDRV_O_RDWR) && (flags & BDRV_O_COPY_ON_READ)) { + if (!bs->read_only && (flags & BDRV_O_COPY_ON_READ)) { bdrv_enable_copy_on_read(bs); } @@ -714,9 +728,6 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file, bs->opaque = g_malloc0(drv->instance_size); bs->enable_write_cache = !!(flags & BDRV_O_CACHE_WB); - open_flags = bdrv_open_flags(bs, flags); - - bs->read_only = !(open_flags & BDRV_O_RDWR); /* Open the image, either directly or using a protocol */ if (drv->bdrv_file_open) { @@ -801,7 +812,7 @@ int bdrv_file_open(BlockDriverState **pbs, const char *filename, /* Find the right block driver */ drvname = qdict_get_try_str(options, "driver"); if (drvname) { - drv = bdrv_find_whitelisted_format(drvname); + drv = bdrv_find_whitelisted_format(drvname, !(flags & BDRV_O_RDWR)); qdict_del(options, "driver"); } else if (filename) { drv = bdrv_find_protocol(filename); diff --git a/blockdev.c b/blockdev.c index d1ec99af73..b9b2d10e6b 100644 --- a/blockdev.c +++ b/blockdev.c @@ -477,7 +477,7 @@ DriveInfo *drive_init(QemuOpts *all_opts, BlockInterfaceType block_default_type) error_printf("\n"); return NULL; } - drv = bdrv_find_whitelisted_format(buf); + drv = bdrv_find_whitelisted_format(buf, ro); if (!drv) { error_report("'%s' invalid format", buf); return NULL; @@ -1096,7 +1096,7 @@ void qmp_change_blockdev(const char *device, const char *filename, } if (format) { - drv = bdrv_find_whitelisted_format(format); + drv = bdrv_find_whitelisted_format(format, bs->read_only); if (!drv) { error_set(errp, QERR_INVALID_BLOCK_FORMAT, format); return; diff --git a/configure b/configure index 70c41b0fad..1654413762 100755 --- a/configure +++ b/configure @@ -123,7 +123,8 @@ interp_prefix="/usr/gnemul/qemu-%M" static="no" cross_prefix="" audio_drv_list="" -block_drv_whitelist="" +block_drv_rw_whitelist="" +block_drv_ro_whitelist="" host_cc="cc" libs_softmmu="" libs_tools="" @@ -708,7 +709,9 @@ for opt do ;; --audio-drv-list=*) audio_drv_list="$optarg" ;; - --block-drv-whitelist=*) block_drv_whitelist=`echo "$optarg" | sed -e 's/,/ /g'` + --block-drv-rw-whitelist=*|--block-drv-whitelist=*) block_drv_rw_whitelist=`echo "$optarg" | sed -e 's/,/ /g'` + ;; + --block-drv-ro-whitelist=*) block_drv_ro_whitelist=`echo "$optarg" | sed -e 's/,/ /g'` ;; --enable-debug-tcg) debug_tcg="yes" ;; @@ -1049,7 +1052,12 @@ echo " --disable-cocoa disable Cocoa (Mac OS X only)" echo " --enable-cocoa enable Cocoa (default on Mac OS X)" echo " --audio-drv-list=LIST set audio drivers list:" echo " Available drivers: $audio_possible_drivers" -echo " --block-drv-whitelist=L set block driver whitelist" +echo " --block-drv-whitelist=L Same as --block-drv-rw-whitelist=L" +echo " --block-drv-rw-whitelist=L" +echo " set block driver read-write whitelist" +echo " (affects only QEMU, not qemu-img)" +echo " --block-drv-ro-whitelist=L" +echo " set block driver read-only whitelist" echo " (affects only QEMU, not qemu-img)" echo " --enable-mixemu enable mixer emulation" echo " --disable-xen disable xen backend driver support" @@ -3481,7 +3489,8 @@ echo "curses support $curses" echo "curl support $curl" echo "mingw32 support $mingw32" echo "Audio drivers $audio_drv_list" -echo "Block whitelist $block_drv_whitelist" +echo "Block whitelist (rw) $block_drv_rw_whitelist" +echo "Block whitelist (ro) $block_drv_ro_whitelist" echo "Mixer emulation $mixemu" echo "VirtFS support $virtfs" echo "VNC support $vnc" @@ -3662,7 +3671,8 @@ fi if test "$audio_win_int" = "yes" ; then echo "CONFIG_AUDIO_WIN_INT=y" >> $config_host_mak fi -echo "CONFIG_BDRV_WHITELIST=$block_drv_whitelist" >> $config_host_mak +echo "CONFIG_BDRV_RW_WHITELIST=$block_drv_rw_whitelist" >> $config_host_mak +echo "CONFIG_BDRV_RO_WHITELIST=$block_drv_ro_whitelist" >> $config_host_mak if test "$mixemu" = "yes" ; then echo "CONFIG_MIXEMU=y" >> $config_host_mak fi diff --git a/hw/block/xen_disk.c b/hw/block/xen_disk.c index 0ac65d4e8f..247f32f4ee 100644 --- a/hw/block/xen_disk.c +++ b/hw/block/xen_disk.c @@ -780,11 +780,13 @@ static int blk_connect(struct XenDevice *xendev) { struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); int pers, index, qflags; + bool readonly = true; /* read-only ? */ qflags = BDRV_O_CACHE_WB | BDRV_O_NATIVE_AIO; if (strcmp(blkdev->mode, "w") == 0) { qflags |= BDRV_O_RDWR; + readonly = false; } /* init qemu block driver */ @@ -795,8 +797,10 @@ static int blk_connect(struct XenDevice *xendev) xen_be_printf(&blkdev->xendev, 2, "create new bdrv (xenbus setup)\n"); blkdev->bs = bdrv_new(blkdev->dev); if (blkdev->bs) { - if (bdrv_open(blkdev->bs, blkdev->filename, NULL, qflags, - bdrv_find_whitelisted_format(blkdev->fileproto)) != 0) { + BlockDriver *drv = bdrv_find_whitelisted_format(blkdev->fileproto, + readonly); + if (bdrv_open(blkdev->bs, + blkdev->filename, NULL, qflags, drv) != 0) { bdrv_delete(blkdev->bs); blkdev->bs = NULL; } diff --git a/include/block/block.h b/include/block/block.h index 1251c5cf9f..5604418a6a 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -124,7 +124,8 @@ void bdrv_init(void); void bdrv_init_with_whitelist(void); BlockDriver *bdrv_find_protocol(const char *filename); BlockDriver *bdrv_find_format(const char *format_name); -BlockDriver *bdrv_find_whitelisted_format(const char *format_name); +BlockDriver *bdrv_find_whitelisted_format(const char *format_name, + bool readonly); int bdrv_create(BlockDriver *drv, const char* filename, QEMUOptionParameter *options); int bdrv_create_file(const char* filename, QEMUOptionParameter *options); diff --git a/scripts/create_config b/scripts/create_config index c471e8caf9..258513a887 100755 --- a/scripts/create_config +++ b/scripts/create_config @@ -34,8 +34,15 @@ case $line in done echo "" ;; - CONFIG_BDRV_WHITELIST=*) - echo "#define CONFIG_BDRV_WHITELIST \\" + CONFIG_BDRV_RW_WHITELIST=*) + echo "#define CONFIG_BDRV_RW_WHITELIST\\" + for drv in ${line#*=}; do + echo " \"${drv}\",\\" + done + echo " NULL" + ;; + CONFIG_BDRV_RO_WHITELIST=*) + echo "#define CONFIG_BDRV_RO_WHITELIST\\" for drv in ${line#*=}; do echo " \"${drv}\",\\" done -- cgit v1.2.1 From ecc1c88efddb376687084c3387c38b3a458c5892 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Tue, 28 May 2013 17:11:34 +0200 Subject: qemu-iotests: make assert_no_active_block_jobs() common Tests 030 and 041 both use query-block-jobs to check whether any block jobs are active. Make this code common so that 'drive-backup' and other new feature tests will be able to reuse it. Suggested-by: Kevin Wolf Signed-off-by: Stefan Hajnoczi Signed-off-by: Kevin Wolf --- tests/qemu-iotests/030 | 54 ++++++++++++++++++-------------------- tests/qemu-iotests/041 | 60 ++++++++++++++++++++----------------------- tests/qemu-iotests/iotests.py | 4 +++ 3 files changed, 57 insertions(+), 61 deletions(-) diff --git a/tests/qemu-iotests/030 b/tests/qemu-iotests/030 index dd4ef11996..03dd6a6a9e 100755 --- a/tests/qemu-iotests/030 +++ b/tests/qemu-iotests/030 @@ -31,10 +31,6 @@ test_img = os.path.join(iotests.test_dir, 'test.img') class ImageStreamingTestCase(iotests.QMPTestCase): '''Abstract base class for image streaming test cases''' - def assert_no_active_streams(self): - result = self.vm.qmp('query-block-jobs') - self.assert_qmp(result, 'return', []) - def cancel_and_wait(self, drive='drive0'): '''Cancel a block job and wait for it to finish''' result = self.vm.qmp('block-job-cancel', device=drive) @@ -48,7 +44,7 @@ class ImageStreamingTestCase(iotests.QMPTestCase): self.assert_qmp(event, 'data/device', drive) cancelled = True - self.assert_no_active_streams() + self.assert_no_active_block_jobs() def create_image(self, name, size): file = open(name, 'w') @@ -77,7 +73,7 @@ class TestSingleDrive(ImageStreamingTestCase): os.remove(backing_img) def test_stream(self): - self.assert_no_active_streams() + self.assert_no_active_block_jobs() result = self.vm.qmp('block-stream', device='drive0') self.assert_qmp(result, 'return', {}) @@ -92,7 +88,7 @@ class TestSingleDrive(ImageStreamingTestCase): self.assert_qmp(event, 'data/len', self.image_len) completed = True - self.assert_no_active_streams() + self.assert_no_active_block_jobs() self.vm.shutdown() self.assertEqual(qemu_io('-c', 'map', backing_img), @@ -100,7 +96,7 @@ class TestSingleDrive(ImageStreamingTestCase): 'image file map does not match backing file after streaming') def test_stream_pause(self): - self.assert_no_active_streams() + self.assert_no_active_block_jobs() result = self.vm.qmp('block-stream', device='drive0') self.assert_qmp(result, 'return', {}) @@ -129,7 +125,7 @@ class TestSingleDrive(ImageStreamingTestCase): self.assert_qmp(event, 'data/len', self.image_len) completed = True - self.assert_no_active_streams() + self.assert_no_active_block_jobs() self.vm.shutdown() self.assertEqual(qemu_io('-c', 'map', backing_img), @@ -137,7 +133,7 @@ class TestSingleDrive(ImageStreamingTestCase): 'image file map does not match backing file after streaming') def test_stream_partial(self): - self.assert_no_active_streams() + self.assert_no_active_block_jobs() result = self.vm.qmp('block-stream', device='drive0', base=mid_img) self.assert_qmp(result, 'return', {}) @@ -152,7 +148,7 @@ class TestSingleDrive(ImageStreamingTestCase): self.assert_qmp(event, 'data/len', self.image_len) completed = True - self.assert_no_active_streams() + self.assert_no_active_block_jobs() self.vm.shutdown() self.assertEqual(qemu_io('-c', 'map', mid_img), @@ -177,7 +173,7 @@ class TestSmallerBackingFile(ImageStreamingTestCase): # If this hangs, then you are missing a fix to complete streaming when the # end of the backing file is reached. def test_stream(self): - self.assert_no_active_streams() + self.assert_no_active_block_jobs() result = self.vm.qmp('block-stream', device='drive0') self.assert_qmp(result, 'return', {}) @@ -192,7 +188,7 @@ class TestSmallerBackingFile(ImageStreamingTestCase): self.assert_qmp(event, 'data/len', self.image_len) completed = True - self.assert_no_active_streams() + self.assert_no_active_block_jobs() self.vm.shutdown() class TestErrors(ImageStreamingTestCase): @@ -243,7 +239,7 @@ class TestEIO(TestErrors): os.remove(self.blkdebug_file) def test_report(self): - self.assert_no_active_streams() + self.assert_no_active_block_jobs() result = self.vm.qmp('block-stream', device='drive0') self.assert_qmp(result, 'return', {}) @@ -265,11 +261,11 @@ class TestEIO(TestErrors): self.assert_qmp(event, 'data/len', self.image_len) completed = True - self.assert_no_active_streams() + self.assert_no_active_block_jobs() self.vm.shutdown() def test_ignore(self): - self.assert_no_active_streams() + self.assert_no_active_block_jobs() result = self.vm.qmp('block-stream', device='drive0', on_error='ignore') self.assert_qmp(result, 'return', {}) @@ -293,11 +289,11 @@ class TestEIO(TestErrors): self.assert_qmp(event, 'data/len', self.image_len) completed = True - self.assert_no_active_streams() + self.assert_no_active_block_jobs() self.vm.shutdown() def test_stop(self): - self.assert_no_active_streams() + self.assert_no_active_block_jobs() result = self.vm.qmp('block-stream', device='drive0', on_error='stop') self.assert_qmp(result, 'return', {}) @@ -331,11 +327,11 @@ class TestEIO(TestErrors): self.assert_qmp(event, 'data/len', self.image_len) completed = True - self.assert_no_active_streams() + self.assert_no_active_block_jobs() self.vm.shutdown() def test_enospc(self): - self.assert_no_active_streams() + self.assert_no_active_block_jobs() result = self.vm.qmp('block-stream', device='drive0', on_error='enospc') self.assert_qmp(result, 'return', {}) @@ -357,7 +353,7 @@ class TestEIO(TestErrors): self.assert_qmp(event, 'data/len', self.image_len) completed = True - self.assert_no_active_streams() + self.assert_no_active_block_jobs() self.vm.shutdown() class TestENOSPC(TestErrors): @@ -379,7 +375,7 @@ class TestENOSPC(TestErrors): os.remove(self.blkdebug_file) def test_enospc(self): - self.assert_no_active_streams() + self.assert_no_active_block_jobs() result = self.vm.qmp('block-stream', device='drive0', on_error='enospc') self.assert_qmp(result, 'return', {}) @@ -413,7 +409,7 @@ class TestENOSPC(TestErrors): self.assert_qmp(event, 'data/len', self.image_len) completed = True - self.assert_no_active_streams() + self.assert_no_active_block_jobs() self.vm.shutdown() class TestStreamStop(ImageStreamingTestCase): @@ -431,7 +427,7 @@ class TestStreamStop(ImageStreamingTestCase): os.remove(backing_img) def test_stream_stop(self): - self.assert_no_active_streams() + self.assert_no_active_block_jobs() result = self.vm.qmp('block-stream', device='drive0') self.assert_qmp(result, 'return', {}) @@ -459,7 +455,7 @@ class TestSetSpeed(ImageStreamingTestCase): # This is a short performance test which is not run by default. # Invoke "IMGFMT=qed ./030 TestSetSpeed.perf_test_throughput" def perf_test_throughput(self): - self.assert_no_active_streams() + self.assert_no_active_block_jobs() result = self.vm.qmp('block-stream', device='drive0') self.assert_qmp(result, 'return', {}) @@ -477,10 +473,10 @@ class TestSetSpeed(ImageStreamingTestCase): self.assert_qmp(event, 'data/len', self.image_len) completed = True - self.assert_no_active_streams() + self.assert_no_active_block_jobs() def test_set_speed(self): - self.assert_no_active_streams() + self.assert_no_active_block_jobs() result = self.vm.qmp('block-stream', device='drive0') self.assert_qmp(result, 'return', {}) @@ -511,12 +507,12 @@ class TestSetSpeed(ImageStreamingTestCase): self.cancel_and_wait() def test_set_speed_invalid(self): - self.assert_no_active_streams() + self.assert_no_active_block_jobs() result = self.vm.qmp('block-stream', device='drive0', speed=-1) self.assert_qmp(result, 'error/class', 'GenericError') - self.assert_no_active_streams() + self.assert_no_active_block_jobs() result = self.vm.qmp('block-stream', device='drive0') self.assert_qmp(result, 'return', {}) diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041 index 720eeff921..ff894271f1 100755 --- a/tests/qemu-iotests/041 +++ b/tests/qemu-iotests/041 @@ -32,10 +32,6 @@ target_img = os.path.join(iotests.test_dir, 'target.img') class ImageMirroringTestCase(iotests.QMPTestCase): '''Abstract base class for image mirroring test cases''' - def assert_no_active_mirrors(self): - result = self.vm.qmp('query-block-jobs') - self.assert_qmp(result, 'return', []) - def cancel_and_wait(self, drive='drive0', wait_ready=True): '''Cancel a block job and wait for it to finish''' if wait_ready: @@ -64,7 +60,7 @@ class ImageMirroringTestCase(iotests.QMPTestCase): self.assert_qmp(event, 'data/len', self.image_len) cancelled = True - self.assert_no_active_mirrors() + self.assert_no_active_block_jobs() def complete_and_wait(self, drive='drive0', wait_ready=True): '''Complete a block job and wait for it to finish''' @@ -91,7 +87,7 @@ class ImageMirroringTestCase(iotests.QMPTestCase): self.assert_qmp(event, 'data/len', self.image_len) completed = True - self.assert_no_active_mirrors() + self.assert_no_active_block_jobs() def create_image(self, name, size): file = open(name, 'w') @@ -142,7 +138,7 @@ class TestSingleDrive(ImageMirroringTestCase): pass def test_complete(self): - self.assert_no_active_mirrors() + self.assert_no_active_block_jobs() result = self.vm.qmp('drive-mirror', device='drive0', sync='full', target=target_img) @@ -156,7 +152,7 @@ class TestSingleDrive(ImageMirroringTestCase): 'target image does not match source after mirroring') def test_cancel(self): - self.assert_no_active_mirrors() + self.assert_no_active_block_jobs() result = self.vm.qmp('drive-mirror', device='drive0', sync='full', target=target_img) @@ -168,7 +164,7 @@ class TestSingleDrive(ImageMirroringTestCase): self.vm.shutdown() def test_cancel_after_ready(self): - self.assert_no_active_mirrors() + self.assert_no_active_block_jobs() result = self.vm.qmp('drive-mirror', device='drive0', sync='full', target=target_img) @@ -182,7 +178,7 @@ class TestSingleDrive(ImageMirroringTestCase): 'target image does not match source after mirroring') def test_pause(self): - self.assert_no_active_mirrors() + self.assert_no_active_block_jobs() result = self.vm.qmp('drive-mirror', device='drive0', sync='full', target=target_img) @@ -208,7 +204,7 @@ class TestSingleDrive(ImageMirroringTestCase): 'target image does not match source after mirroring') def test_small_buffer(self): - self.assert_no_active_mirrors() + self.assert_no_active_block_jobs() # A small buffer is rounded up automatically result = self.vm.qmp('drive-mirror', device='drive0', sync='full', @@ -223,7 +219,7 @@ class TestSingleDrive(ImageMirroringTestCase): 'target image does not match source after mirroring') def test_small_buffer2(self): - self.assert_no_active_mirrors() + self.assert_no_active_block_jobs() qemu_img('create', '-f', iotests.imgfmt, '-o', 'cluster_size=%d,size=%d' % (TestSingleDrive.image_len, TestSingleDrive.image_len), target_img) @@ -239,7 +235,7 @@ class TestSingleDrive(ImageMirroringTestCase): 'target image does not match source after mirroring') def test_large_cluster(self): - self.assert_no_active_mirrors() + self.assert_no_active_block_jobs() qemu_img('create', '-f', iotests.imgfmt, '-o', 'cluster_size=%d,backing_file=%s' % (TestSingleDrive.image_len, backing_img), target_img) @@ -294,7 +290,7 @@ class TestMirrorNoBacking(ImageMirroringTestCase): os.remove(target_img) def test_complete(self): - self.assert_no_active_mirrors() + self.assert_no_active_block_jobs() qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, target_img) result = self.vm.qmp('drive-mirror', device='drive0', sync='full', @@ -309,7 +305,7 @@ class TestMirrorNoBacking(ImageMirroringTestCase): 'target image does not match source after mirroring') def test_cancel(self): - self.assert_no_active_mirrors() + self.assert_no_active_block_jobs() qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, target_img) result = self.vm.qmp('drive-mirror', device='drive0', sync='full', @@ -324,7 +320,7 @@ class TestMirrorNoBacking(ImageMirroringTestCase): 'target image does not match source after mirroring') def test_large_cluster(self): - self.assert_no_active_mirrors() + self.assert_no_active_block_jobs() # qemu-img create fails if the image is not there qemu_img('create', '-f', iotests.imgfmt, '-o', 'size=%d' @@ -365,7 +361,7 @@ class TestMirrorResized(ImageMirroringTestCase): pass def test_complete_top(self): - self.assert_no_active_mirrors() + self.assert_no_active_block_jobs() result = self.vm.qmp('drive-mirror', device='drive0', sync='top', target=target_img) @@ -379,7 +375,7 @@ class TestMirrorResized(ImageMirroringTestCase): 'target image does not match source after mirroring') def test_complete_full(self): - self.assert_no_active_mirrors() + self.assert_no_active_block_jobs() result = self.vm.qmp('drive-mirror', device='drive0', sync='full', target=target_img) @@ -443,7 +439,7 @@ new_state = "1" os.remove(self.blkdebug_file) def test_report_read(self): - self.assert_no_active_mirrors() + self.assert_no_active_block_jobs() result = self.vm.qmp('drive-mirror', device='drive0', sync='full', target=target_img) @@ -467,11 +463,11 @@ new_state = "1" self.assert_qmp(event, 'data/len', self.image_len) completed = True - self.assert_no_active_mirrors() + self.assert_no_active_block_jobs() self.vm.shutdown() def test_ignore_read(self): - self.assert_no_active_mirrors() + self.assert_no_active_block_jobs() result = self.vm.qmp('drive-mirror', device='drive0', sync='full', target=target_img, on_source_error='ignore') @@ -487,7 +483,7 @@ new_state = "1" self.vm.shutdown() def test_large_cluster(self): - self.assert_no_active_mirrors() + self.assert_no_active_block_jobs() # Test COW into the target image. The first half of the # cluster at MIRROR_GRANULARITY has to be copied from @@ -513,7 +509,7 @@ new_state = "1" 'target image does not match source after mirroring') def test_stop_read(self): - self.assert_no_active_mirrors() + self.assert_no_active_block_jobs() result = self.vm.qmp('drive-mirror', device='drive0', sync='full', target=target_img, on_source_error='stop') @@ -544,7 +540,7 @@ new_state = "1" self.assert_qmp(result, 'return[0]/io-status', 'ok') self.complete_and_wait(wait_ready=False) - self.assert_no_active_mirrors() + self.assert_no_active_block_jobs() self.vm.shutdown() class TestWriteErrors(ImageMirroringTestCase): @@ -594,7 +590,7 @@ new_state = "1" os.remove(self.blkdebug_file) def test_report_write(self): - self.assert_no_active_mirrors() + self.assert_no_active_block_jobs() result = self.vm.qmp('drive-mirror', device='drive0', sync='full', mode='existing', target=self.target_img) @@ -618,11 +614,11 @@ new_state = "1" self.assert_qmp(event, 'data/len', self.image_len) completed = True - self.assert_no_active_mirrors() + self.assert_no_active_block_jobs() self.vm.shutdown() def test_ignore_write(self): - self.assert_no_active_mirrors() + self.assert_no_active_block_jobs() result = self.vm.qmp('drive-mirror', device='drive0', sync='full', mode='existing', target=self.target_img, @@ -639,7 +635,7 @@ new_state = "1" self.vm.shutdown() def test_stop_write(self): - self.assert_no_active_mirrors() + self.assert_no_active_block_jobs() result = self.vm.qmp('drive-mirror', device='drive0', sync='full', mode='existing', target=self.target_img, @@ -671,7 +667,7 @@ new_state = "1" ready = True self.complete_and_wait(wait_ready=False) - self.assert_no_active_mirrors() + self.assert_no_active_block_jobs() self.vm.shutdown() class TestSetSpeed(ImageMirroringTestCase): @@ -690,7 +686,7 @@ class TestSetSpeed(ImageMirroringTestCase): os.remove(target_img) def test_set_speed(self): - self.assert_no_active_mirrors() + self.assert_no_active_block_jobs() result = self.vm.qmp('drive-mirror', device='drive0', sync='full', target=target_img) @@ -723,13 +719,13 @@ class TestSetSpeed(ImageMirroringTestCase): self.cancel_and_wait() def test_set_speed_invalid(self): - self.assert_no_active_mirrors() + self.assert_no_active_block_jobs() result = self.vm.qmp('drive-mirror', device='drive0', sync='full', target=target_img, speed=-1) self.assert_qmp(result, 'error/class', 'GenericError') - self.assert_no_active_mirrors() + self.assert_no_active_block_jobs() result = self.vm.qmp('drive-mirror', device='drive0', sync='full', target=target_img) diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index 569ca3d804..0e7862cee5 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -170,6 +170,10 @@ class QMPTestCase(unittest.TestCase): result = self.dictpath(d, path) self.assertEqual(result, value, 'values not equal "%s" and "%s"' % (str(result), str(value))) + def assert_no_active_block_jobs(self): + result = self.vm.qmp('query-block-jobs') + self.assert_qmp(result, 'return', []) + def notrun(reason): '''Skip this test suite''' # Each test in qemu-iotests has a number ("seq") -- cgit v1.2.1 From 2575fe16d257a1fb5f452391b868b3c3263a9aca Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Tue, 28 May 2013 17:11:35 +0200 Subject: qemu-iotests: make cancel_and_wait() common The cancel_and_wait() function has been duplicated in 030 and 041. Move it into iotests.py and let it return the event so tests can perform additional asserts. Note that 041's cancel_and_wait(wait_ready=True) is replaced by wait_ready_and_cancel(), which uses the new wait_ready() and cancel_and_wait() underneath. Suggested-by: Kevin Wolf Signed-off-by: Stefan Hajnoczi Signed-off-by: Kevin Wolf --- tests/qemu-iotests/030 | 15 ----------- tests/qemu-iotests/041 | 58 +++++++++++++++---------------------------- tests/qemu-iotests/iotests.py | 18 ++++++++++++++ 3 files changed, 38 insertions(+), 53 deletions(-) diff --git a/tests/qemu-iotests/030 b/tests/qemu-iotests/030 index 03dd6a6a9e..1f5509566c 100755 --- a/tests/qemu-iotests/030 +++ b/tests/qemu-iotests/030 @@ -31,21 +31,6 @@ test_img = os.path.join(iotests.test_dir, 'test.img') class ImageStreamingTestCase(iotests.QMPTestCase): '''Abstract base class for image streaming test cases''' - def cancel_and_wait(self, drive='drive0'): - '''Cancel a block job and wait for it to finish''' - result = self.vm.qmp('block-job-cancel', device=drive) - self.assert_qmp(result, 'return', {}) - - cancelled = False - while not cancelled: - for event in self.vm.get_qmp_events(wait=True): - if event['event'] == 'BLOCK_JOB_CANCELLED': - self.assert_qmp(event, 'data/type', 'stream') - self.assert_qmp(event, 'data/device', drive) - cancelled = True - - self.assert_no_active_block_jobs() - def create_image(self, name, size): file = open(name, 'w') i = 0 diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041 index ff894271f1..c4ce75e77a 100755 --- a/tests/qemu-iotests/041 +++ b/tests/qemu-iotests/041 @@ -32,46 +32,28 @@ target_img = os.path.join(iotests.test_dir, 'target.img') class ImageMirroringTestCase(iotests.QMPTestCase): '''Abstract base class for image mirroring test cases''' - def cancel_and_wait(self, drive='drive0', wait_ready=True): - '''Cancel a block job and wait for it to finish''' - if wait_ready: - ready = False - while not ready: - for event in self.vm.get_qmp_events(wait=True): - if event['event'] == 'BLOCK_JOB_READY': - self.assert_qmp(event, 'data/type', 'mirror') - self.assert_qmp(event, 'data/device', drive) - ready = True - - result = self.vm.qmp('block-job-cancel', device=drive, - force=not wait_ready) - self.assert_qmp(result, 'return', {}) - - cancelled = False - while not cancelled: + def wait_ready(self, drive='drive0'): + '''Wait until a block job BLOCK_JOB_READY event''' + ready = False + while not ready: for event in self.vm.get_qmp_events(wait=True): - if event['event'] == 'BLOCK_JOB_COMPLETED' or \ - event['event'] == 'BLOCK_JOB_CANCELLED': + if event['event'] == 'BLOCK_JOB_READY': self.assert_qmp(event, 'data/type', 'mirror') self.assert_qmp(event, 'data/device', drive) - if wait_ready: - self.assertEquals(event['event'], 'BLOCK_JOB_COMPLETED') - self.assert_qmp(event, 'data/offset', self.image_len) - self.assert_qmp(event, 'data/len', self.image_len) - cancelled = True + ready = True - self.assert_no_active_block_jobs() + def wait_ready_and_cancel(self, drive='drive0'): + self.wait_ready(drive) + event = self.cancel_and_wait() + self.assertEquals(event['event'], 'BLOCK_JOB_COMPLETED') + self.assert_qmp(event, 'data/type', 'mirror') + self.assert_qmp(event, 'data/offset', self.image_len) + self.assert_qmp(event, 'data/len', self.image_len) def complete_and_wait(self, drive='drive0', wait_ready=True): '''Complete a block job and wait for it to finish''' if wait_ready: - ready = False - while not ready: - for event in self.vm.get_qmp_events(wait=True): - if event['event'] == 'BLOCK_JOB_READY': - self.assert_qmp(event, 'data/type', 'mirror') - self.assert_qmp(event, 'data/device', drive) - ready = True + self.wait_ready() result = self.vm.qmp('block-job-complete', device=drive) self.assert_qmp(result, 'return', {}) @@ -158,7 +140,7 @@ class TestSingleDrive(ImageMirroringTestCase): target=target_img) self.assert_qmp(result, 'return', {}) - self.cancel_and_wait(wait_ready=False) + self.cancel_and_wait(force=True) result = self.vm.qmp('query-block') self.assert_qmp(result, 'return[0]/inserted/file', test_img) self.vm.shutdown() @@ -170,7 +152,7 @@ class TestSingleDrive(ImageMirroringTestCase): target=target_img) self.assert_qmp(result, 'return', {}) - self.cancel_and_wait() + self.wait_ready_and_cancel() result = self.vm.qmp('query-block') self.assert_qmp(result, 'return[0]/inserted/file', test_img) self.vm.shutdown() @@ -312,7 +294,7 @@ class TestMirrorNoBacking(ImageMirroringTestCase): mode='existing', target=target_img) self.assert_qmp(result, 'return', {}) - self.cancel_and_wait() + self.wait_ready_and_cancel() result = self.vm.qmp('query-block') self.assert_qmp(result, 'return[0]/inserted/file', test_img) self.vm.shutdown() @@ -705,7 +687,7 @@ class TestSetSpeed(ImageMirroringTestCase): self.assert_qmp(result, 'return[0]/device', 'drive0') self.assert_qmp(result, 'return[0]/speed', 8 * 1024 * 1024) - self.cancel_and_wait() + self.wait_ready_and_cancel() # Check setting speed in drive-mirror works result = self.vm.qmp('drive-mirror', device='drive0', sync='full', @@ -716,7 +698,7 @@ class TestSetSpeed(ImageMirroringTestCase): self.assert_qmp(result, 'return[0]/device', 'drive0') self.assert_qmp(result, 'return[0]/speed', 4 * 1024 * 1024) - self.cancel_and_wait() + self.wait_ready_and_cancel() def test_set_speed_invalid(self): self.assert_no_active_block_jobs() @@ -734,7 +716,7 @@ class TestSetSpeed(ImageMirroringTestCase): result = self.vm.qmp('block-job-set-speed', device='drive0', speed=-1) self.assert_qmp(result, 'error/class', 'GenericError') - self.cancel_and_wait() + self.wait_ready_and_cancel() if __name__ == '__main__': iotests.main(supported_fmts=['qcow2', 'qed']) diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index 0e7862cee5..bc9c71b979 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -174,6 +174,24 @@ class QMPTestCase(unittest.TestCase): result = self.vm.qmp('query-block-jobs') self.assert_qmp(result, 'return', []) + def cancel_and_wait(self, drive='drive0', force=False): + '''Cancel a block job and wait for it to finish, returning the event''' + result = self.vm.qmp('block-job-cancel', device=drive, force=force) + self.assert_qmp(result, 'return', {}) + + cancelled = False + result = None + while not cancelled: + for event in self.vm.get_qmp_events(wait=True): + if event['event'] == 'BLOCK_JOB_COMPLETED' or \ + event['event'] == 'BLOCK_JOB_CANCELLED': + self.assert_qmp(event, 'data/device', drive) + result = event + cancelled = True + + self.assert_no_active_block_jobs() + return result + def notrun(reason): '''Skip this test suite''' # Each test in qemu-iotests has a number ("seq") -- cgit v1.2.1 From 3a3918c396c5caeab35a7f51af905172a13d996a Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Tue, 28 May 2013 17:11:36 +0200 Subject: qemu-iotests: make compare_images() common The iotests.compare_images() function returns True if two image files have the identical data. Previously this was implemented by converting images to raw and then comparing their contents using Python. Since "qemu-img compare" is now available and is more efficient, switch to it. This function will be reused by the 'drive-backup' test case. Suggested-by: Kevin Wolf Signed-off-by: Stefan Hajnoczi Signed-off-by: Kevin Wolf --- tests/qemu-iotests/041 | 41 ++++++++++------------------------------- tests/qemu-iotests/iotests.py | 5 +++++ 2 files changed, 15 insertions(+), 31 deletions(-) diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041 index c4ce75e77a..77020741e0 100755 --- a/tests/qemu-iotests/041 +++ b/tests/qemu-iotests/041 @@ -80,27 +80,6 @@ class ImageMirroringTestCase(iotests.QMPTestCase): i = i + 512 file.close() - def compare_images(self, img1, img2): - try: - qemu_img('convert', '-f', iotests.imgfmt, '-O', 'raw', img1, img1 + '.raw') - qemu_img('convert', '-f', iotests.imgfmt, '-O', 'raw', img2, img2 + '.raw') - file1 = open(img1 + '.raw', 'r') - file2 = open(img2 + '.raw', 'r') - return file1.read() == file2.read() - finally: - if file1 is not None: - file1.close() - if file2 is not None: - file2.close() - try: - os.remove(img1 + '.raw') - except OSError: - pass - try: - os.remove(img2 + '.raw') - except OSError: - pass - class TestSingleDrive(ImageMirroringTestCase): image_len = 1 * 1024 * 1024 # MB @@ -130,7 +109,7 @@ class TestSingleDrive(ImageMirroringTestCase): result = self.vm.qmp('query-block') self.assert_qmp(result, 'return[0]/inserted/file', target_img) self.vm.shutdown() - self.assertTrue(self.compare_images(test_img, target_img), + self.assertTrue(iotests.compare_images(test_img, target_img), 'target image does not match source after mirroring') def test_cancel(self): @@ -156,7 +135,7 @@ class TestSingleDrive(ImageMirroringTestCase): result = self.vm.qmp('query-block') self.assert_qmp(result, 'return[0]/inserted/file', test_img) self.vm.shutdown() - self.assertTrue(self.compare_images(test_img, target_img), + self.assertTrue(iotests.compare_images(test_img, target_img), 'target image does not match source after mirroring') def test_pause(self): @@ -182,7 +161,7 @@ class TestSingleDrive(ImageMirroringTestCase): self.complete_and_wait() self.vm.shutdown() - self.assertTrue(self.compare_images(test_img, target_img), + self.assertTrue(iotests.compare_images(test_img, target_img), 'target image does not match source after mirroring') def test_small_buffer(self): @@ -197,7 +176,7 @@ class TestSingleDrive(ImageMirroringTestCase): result = self.vm.qmp('query-block') self.assert_qmp(result, 'return[0]/inserted/file', target_img) self.vm.shutdown() - self.assertTrue(self.compare_images(test_img, target_img), + self.assertTrue(iotests.compare_images(test_img, target_img), 'target image does not match source after mirroring') def test_small_buffer2(self): @@ -213,7 +192,7 @@ class TestSingleDrive(ImageMirroringTestCase): result = self.vm.qmp('query-block') self.assert_qmp(result, 'return[0]/inserted/file', target_img) self.vm.shutdown() - self.assertTrue(self.compare_images(test_img, target_img), + self.assertTrue(iotests.compare_images(test_img, target_img), 'target image does not match source after mirroring') def test_large_cluster(self): @@ -229,7 +208,7 @@ class TestSingleDrive(ImageMirroringTestCase): result = self.vm.qmp('query-block') self.assert_qmp(result, 'return[0]/inserted/file', target_img) self.vm.shutdown() - self.assertTrue(self.compare_images(test_img, target_img), + self.assertTrue(iotests.compare_images(test_img, target_img), 'target image does not match source after mirroring') def test_medium_not_found(self): @@ -256,7 +235,7 @@ class TestMirrorNoBacking(ImageMirroringTestCase): def compare_images(self, img1, img2): self.create_image(target_backing_img, TestMirrorNoBacking.image_len) - return ImageMirroringTestCase.compare_images(self, img1, img2) + return iotests.compare_images(img1, img2) def setUp(self): self.create_image(backing_img, TestMirrorNoBacking.image_len) @@ -353,7 +332,7 @@ class TestMirrorResized(ImageMirroringTestCase): result = self.vm.qmp('query-block') self.assert_qmp(result, 'return[0]/inserted/file', target_img) self.vm.shutdown() - self.assertTrue(self.compare_images(test_img, target_img), + self.assertTrue(iotests.compare_images(test_img, target_img), 'target image does not match source after mirroring') def test_complete_full(self): @@ -367,7 +346,7 @@ class TestMirrorResized(ImageMirroringTestCase): result = self.vm.qmp('query-block') self.assert_qmp(result, 'return[0]/inserted/file', target_img) self.vm.shutdown() - self.assertTrue(self.compare_images(test_img, target_img), + self.assertTrue(iotests.compare_images(test_img, target_img), 'target image does not match source after mirroring') class TestReadErrors(ImageMirroringTestCase): @@ -487,7 +466,7 @@ new_state = "1" # Detach blkdebug to compare images successfully qemu_img('rebase', '-f', iotests.imgfmt, '-u', '-b', backing_img, test_img) - self.assertTrue(self.compare_images(test_img, target_img), + self.assertTrue(iotests.compare_images(test_img, target_img), 'target image does not match source after mirroring') def test_stop_read(self): diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index bc9c71b979..733b82b42b 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -51,6 +51,11 @@ def qemu_io(*args): args = qemu_io_args + list(args) return subprocess.Popen(args, stdout=subprocess.PIPE).communicate()[0] +def compare_images(img1, img2): + '''Return True if two image files are identical''' + return qemu_img('compare', '-f', imgfmt, + '-F', imgfmt, img1, img2) == 0 + class VM(object): '''A QEMU VM''' -- cgit v1.2.1 From 2499a096a2427f0a5c71750c9f79cf2d2d2d60f4 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Tue, 28 May 2013 17:11:37 +0200 Subject: qemu-iotests: make create_image() common Both 030 and 041 use create_image(). Move it to iotests.py. Also drop ImageStreamingTestCase since the class now has no methods. Suggested-by: Kevin Wolf Signed-off-by: Stefan Hajnoczi Signed-off-by: Kevin Wolf --- tests/qemu-iotests/030 | 32 +++++++++----------------------- tests/qemu-iotests/041 | 24 +++++++----------------- tests/qemu-iotests/iotests.py | 11 +++++++++++ 3 files changed, 27 insertions(+), 40 deletions(-) diff --git a/tests/qemu-iotests/030 b/tests/qemu-iotests/030 index 1f5509566c..ae56f3b808 100755 --- a/tests/qemu-iotests/030 +++ b/tests/qemu-iotests/030 @@ -22,30 +22,16 @@ import time import os import iotests from iotests import qemu_img, qemu_io -import struct backing_img = os.path.join(iotests.test_dir, 'backing.img') mid_img = os.path.join(iotests.test_dir, 'mid.img') test_img = os.path.join(iotests.test_dir, 'test.img') -class ImageStreamingTestCase(iotests.QMPTestCase): - '''Abstract base class for image streaming test cases''' - - def create_image(self, name, size): - file = open(name, 'w') - i = 0 - while i < size: - sector = struct.pack('>l504xl', i / 512, i / 512) - file.write(sector) - i = i + 512 - file.close() - - -class TestSingleDrive(ImageStreamingTestCase): +class TestSingleDrive(iotests.QMPTestCase): image_len = 1 * 1024 * 1024 # MB def setUp(self): - self.create_image(backing_img, TestSingleDrive.image_len) + iotests.create_image(backing_img, TestSingleDrive.image_len) qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, mid_img) qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % mid_img, test_img) self.vm = iotests.VM().add_drive(test_img) @@ -145,12 +131,12 @@ class TestSingleDrive(ImageStreamingTestCase): self.assert_qmp(result, 'error/class', 'DeviceNotFound') -class TestSmallerBackingFile(ImageStreamingTestCase): +class TestSmallerBackingFile(iotests.QMPTestCase): backing_len = 1 * 1024 * 1024 # MB image_len = 2 * backing_len def setUp(self): - self.create_image(backing_img, self.backing_len) + iotests.create_image(backing_img, self.backing_len) qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img, str(self.image_len)) self.vm = iotests.VM().add_drive(test_img) self.vm.launch() @@ -176,7 +162,7 @@ class TestSmallerBackingFile(ImageStreamingTestCase): self.assert_no_active_block_jobs() self.vm.shutdown() -class TestErrors(ImageStreamingTestCase): +class TestErrors(iotests.QMPTestCase): image_len = 2 * 1024 * 1024 # MB # this should match STREAM_BUFFER_SIZE/512 in block/stream.c @@ -208,7 +194,7 @@ new_state = "1" class TestEIO(TestErrors): def setUp(self): self.blkdebug_file = backing_img + ".blkdebug" - self.create_image(backing_img, TestErrors.image_len) + iotests.create_image(backing_img, TestErrors.image_len) self.create_blkdebug_file(self.blkdebug_file, "read_aio", 5) qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=blkdebug:%s:%s,backing_fmt=raw' @@ -344,7 +330,7 @@ class TestEIO(TestErrors): class TestENOSPC(TestErrors): def setUp(self): self.blkdebug_file = backing_img + ".blkdebug" - self.create_image(backing_img, TestErrors.image_len) + iotests.create_image(backing_img, TestErrors.image_len) self.create_blkdebug_file(self.blkdebug_file, "read_aio", 28) qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=blkdebug:%s:%s,backing_fmt=raw' @@ -397,7 +383,7 @@ class TestENOSPC(TestErrors): self.assert_no_active_block_jobs() self.vm.shutdown() -class TestStreamStop(ImageStreamingTestCase): +class TestStreamStop(iotests.QMPTestCase): image_len = 8 * 1024 * 1024 * 1024 # GB def setUp(self): @@ -423,7 +409,7 @@ class TestStreamStop(ImageStreamingTestCase): self.cancel_and_wait() -class TestSetSpeed(ImageStreamingTestCase): +class TestSetSpeed(iotests.QMPTestCase): image_len = 80 * 1024 * 1024 # MB def setUp(self): diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041 index 77020741e0..1e923e70ca 100755 --- a/tests/qemu-iotests/041 +++ b/tests/qemu-iotests/041 @@ -22,7 +22,6 @@ import time import os import iotests from iotests import qemu_img, qemu_io -import struct backing_img = os.path.join(iotests.test_dir, 'backing.img') target_backing_img = os.path.join(iotests.test_dir, 'target-backing.img') @@ -71,20 +70,11 @@ class ImageMirroringTestCase(iotests.QMPTestCase): self.assert_no_active_block_jobs() - def create_image(self, name, size): - file = open(name, 'w') - i = 0 - while i < size: - sector = struct.pack('>l504xl', i / 512, i / 512) - file.write(sector) - i = i + 512 - file.close() - class TestSingleDrive(ImageMirroringTestCase): image_len = 1 * 1024 * 1024 # MB def setUp(self): - self.create_image(backing_img, TestSingleDrive.image_len) + iotests.create_image(backing_img, TestSingleDrive.image_len) qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img) self.vm = iotests.VM().add_drive(test_img) self.vm.launch() @@ -230,15 +220,15 @@ class TestMirrorNoBacking(ImageMirroringTestCase): image_len = 2 * 1024 * 1024 # MB def complete_and_wait(self, drive='drive0', wait_ready=True): - self.create_image(target_backing_img, TestMirrorNoBacking.image_len) + iotests.create_image(target_backing_img, TestMirrorNoBacking.image_len) return ImageMirroringTestCase.complete_and_wait(self, drive, wait_ready) def compare_images(self, img1, img2): - self.create_image(target_backing_img, TestMirrorNoBacking.image_len) + iotests.create_image(target_backing_img, TestMirrorNoBacking.image_len) return iotests.compare_images(img1, img2) def setUp(self): - self.create_image(backing_img, TestMirrorNoBacking.image_len) + iotests.create_image(backing_img, TestMirrorNoBacking.image_len) qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img) self.vm = iotests.VM().add_drive(test_img) self.vm.launch() @@ -306,7 +296,7 @@ class TestMirrorResized(ImageMirroringTestCase): image_len = 2 * 1024 * 1024 # MB def setUp(self): - self.create_image(backing_img, TestMirrorResized.backing_len) + iotests.create_image(backing_img, TestMirrorResized.backing_len) qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img) qemu_img('resize', test_img, '2M') self.vm = iotests.VM().add_drive(test_img) @@ -381,7 +371,7 @@ new_state = "1" def setUp(self): self.blkdebug_file = backing_img + ".blkdebug" - self.create_image(backing_img, TestReadErrors.image_len) + iotests.create_image(backing_img, TestReadErrors.image_len) self.create_blkdebug_file(self.blkdebug_file, "read_aio", 5) qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=blkdebug:%s:%s,backing_fmt=raw' @@ -536,7 +526,7 @@ new_state = "1" def setUp(self): self.blkdebug_file = target_img + ".blkdebug" - self.create_image(backing_img, TestWriteErrors.image_len) + iotests.create_image(backing_img, TestWriteErrors.image_len) self.create_blkdebug_file(self.blkdebug_file, "write_aio", 5) qemu_img('create', '-f', iotests.imgfmt, '-obacking_file=%s' %(backing_img), test_img) self.vm = iotests.VM().add_drive(test_img) diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index 733b82b42b..8a8f1814bd 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -23,6 +23,7 @@ import string import unittest import sys; sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'QMP')) import qmp +import struct __all__ = ['imgfmt', 'imgproto', 'test_dir' 'qemu_img', 'qemu_io', 'VM', 'QMPTestCase', 'notrun', 'main'] @@ -56,6 +57,16 @@ def compare_images(img1, img2): return qemu_img('compare', '-f', imgfmt, '-F', imgfmt, img1, img2) == 0 +def create_image(name, size): + '''Create a fully-allocated raw image with sector markers''' + file = open(name, 'w') + i = 0 + while i < size: + sector = struct.pack('>l504xl', i / 512, i / 512) + file.write(sector) + i = i + 512 + file.close() + class VM(object): '''A QEMU VM''' -- cgit v1.2.1 From 29d782710f87f01991bfc85cd9bef7d15280a5e2 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Sat, 25 May 2013 11:09:42 +0800 Subject: block: drop bs_snapshots global variable The bs_snapshots global variable points to the BlockDriverState which will be used to save vmstate. This is really a savevm.c concept but was moved into block.c:bdrv_snapshots() when it became clear that hotplug could result in a dangling pointer. While auditing the block layer's global state I came upon bs_snapshots and realized that a variable is not necessary here. Simply find the first BlockDriverState capable of internal snapshots each time this is needed. The behavior of bdrv_snapshots() is preserved across hotplug because new drives are always appended to the bdrv_states list. This means that calling the new find_vmstate_bs() function is idempotent - it returns the same BlockDriverState unless it was hot-unplugged. Signed-off-by: Stefan Hajnoczi Reviewed-by: Eric Blake Reviewed-by: Wenchao Xia Signed-off-by: Wenchao Xia Signed-off-by: Kevin Wolf --- block.c | 28 ---------------------------- include/block/block.h | 1 - savevm.c | 19 +++++++++++++++---- 3 files changed, 15 insertions(+), 33 deletions(-) diff --git a/block.c b/block.c index 65c0b60736..e046a313c5 100644 --- a/block.c +++ b/block.c @@ -99,9 +99,6 @@ static QTAILQ_HEAD(, BlockDriverState) bdrv_states = static QLIST_HEAD(, BlockDriver) bdrv_drivers = QLIST_HEAD_INITIALIZER(bdrv_drivers); -/* The device to use for VM snapshots */ -static BlockDriverState *bs_snapshots; - /* If non-zero, use only whitelisted block drivers */ static int use_bdrv_whitelist; @@ -1368,9 +1365,6 @@ void bdrv_close(BlockDriverState *bs) notifier_list_notify(&bs->close_notifiers, bs); if (bs->drv) { - if (bs == bs_snapshots) { - bs_snapshots = NULL; - } if (bs->backing_hd) { bdrv_delete(bs->backing_hd); bs->backing_hd = NULL; @@ -1602,7 +1596,6 @@ void bdrv_delete(BlockDriverState *bs) bdrv_close(bs); - assert(bs != bs_snapshots); g_free(bs); } @@ -1646,9 +1639,6 @@ void bdrv_set_dev_ops(BlockDriverState *bs, const BlockDevOps *ops, { bs->dev_ops = ops; bs->dev_opaque = opaque; - if (bdrv_dev_has_removable_media(bs) && bs == bs_snapshots) { - bs_snapshots = NULL; - } } void bdrv_emit_qmp_error_event(const BlockDriverState *bdrv, @@ -3392,24 +3382,6 @@ int bdrv_is_snapshot(BlockDriverState *bs) return !!(bs->open_flags & BDRV_O_SNAPSHOT); } -BlockDriverState *bdrv_snapshots(void) -{ - BlockDriverState *bs; - - if (bs_snapshots) { - return bs_snapshots; - } - - bs = NULL; - while ((bs = bdrv_next(bs))) { - if (bdrv_can_snapshot(bs)) { - bs_snapshots = bs; - return bs; - } - } - return NULL; -} - int bdrv_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) { diff --git a/include/block/block.h b/include/block/block.h index 5604418a6a..896553658f 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -333,7 +333,6 @@ BlockInfo *bdrv_query_info(BlockDriverState *s); BlockStats *bdrv_query_stats(const BlockDriverState *bs); int bdrv_can_snapshot(BlockDriverState *bs); int bdrv_is_snapshot(BlockDriverState *bs); -BlockDriverState *bdrv_snapshots(void); int bdrv_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info); int bdrv_snapshot_goto(BlockDriverState *bs, diff --git a/savevm.c b/savevm.c index 4e0fab6cd6..b11dbf6210 100644 --- a/savevm.c +++ b/savevm.c @@ -2262,6 +2262,17 @@ out: return ret; } +static BlockDriverState *find_vmstate_bs(void) +{ + BlockDriverState *bs = NULL; + while ((bs = bdrv_next(bs))) { + if (bdrv_can_snapshot(bs)) { + return bs; + } + } + return NULL; +} + static int bdrv_snapshot_find(BlockDriverState *bs, QEMUSnapshotInfo *sn_info, const char *name) { @@ -2338,7 +2349,7 @@ void do_savevm(Monitor *mon, const QDict *qdict) } } - bs = bdrv_snapshots(); + bs = find_vmstate_bs(); if (!bs) { monitor_printf(mon, "No block device can accept snapshots\n"); return; @@ -2440,7 +2451,7 @@ int load_vmstate(const char *name) QEMUFile *f; int ret; - bs_vm_state = bdrv_snapshots(); + bs_vm_state = find_vmstate_bs(); if (!bs_vm_state) { error_report("No block device supports snapshots"); return -ENOTSUP; @@ -2519,7 +2530,7 @@ void do_delvm(Monitor *mon, const QDict *qdict) int ret; const char *name = qdict_get_str(qdict, "name"); - bs = bdrv_snapshots(); + bs = find_vmstate_bs(); if (!bs) { monitor_printf(mon, "No block device supports snapshots\n"); return; @@ -2551,7 +2562,7 @@ void do_info_snapshots(Monitor *mon, const QDict *qdict) int *available_snapshots; char buf[256]; - bs = bdrv_snapshots(); + bs = find_vmstate_bs(); if (!bs) { monitor_printf(mon, "No available block device supports snapshots\n"); return; -- cgit v1.2.1 From de08c606f9ddafe647b6843e2b10a6d6030b0fc0 Mon Sep 17 00:00:00 2001 From: Wenchao Xia Date: Sat, 25 May 2013 11:09:43 +0800 Subject: block: move snapshot code in block.c to block/snapshot.c All snapshot related code, except bdrv_snapshot_dump() and bdrv_is_snapshot(), is moved to block/snapshot.c. bdrv_snapshot_dump() will be moved to another file later. bdrv_is_snapshot() is not related with internal snapshot. It also fixes small code style errors reported by check script. Signed-off-by: Wenchao Xia Signed-off-by: Kevin Wolf --- block.c | 100 ------------------------------ block/Makefile.objs | 1 + block/snapshot.c | 157 +++++++++++++++++++++++++++++++++++++++++++++++ include/block/block.h | 26 ++------ include/block/snapshot.h | 53 ++++++++++++++++ savevm.c | 23 +------ 6 files changed, 217 insertions(+), 143 deletions(-) create mode 100644 block/snapshot.c create mode 100644 include/block/snapshot.h diff --git a/block.c b/block.c index e046a313c5..6212aae534 100644 --- a/block.c +++ b/block.c @@ -3357,111 +3357,11 @@ bool bdrv_debug_is_suspended(BlockDriverState *bs, const char *tag) return false; } -/**************************************************************/ -/* handling of snapshots */ - -int bdrv_can_snapshot(BlockDriverState *bs) -{ - BlockDriver *drv = bs->drv; - if (!drv || !bdrv_is_inserted(bs) || bdrv_is_read_only(bs)) { - return 0; - } - - if (!drv->bdrv_snapshot_create) { - if (bs->file != NULL) { - return bdrv_can_snapshot(bs->file); - } - return 0; - } - - return 1; -} - int bdrv_is_snapshot(BlockDriverState *bs) { return !!(bs->open_flags & BDRV_O_SNAPSHOT); } -int bdrv_snapshot_create(BlockDriverState *bs, - QEMUSnapshotInfo *sn_info) -{ - BlockDriver *drv = bs->drv; - if (!drv) - return -ENOMEDIUM; - if (drv->bdrv_snapshot_create) - return drv->bdrv_snapshot_create(bs, sn_info); - if (bs->file) - return bdrv_snapshot_create(bs->file, sn_info); - return -ENOTSUP; -} - -int bdrv_snapshot_goto(BlockDriverState *bs, - const char *snapshot_id) -{ - BlockDriver *drv = bs->drv; - int ret, open_ret; - - if (!drv) - return -ENOMEDIUM; - if (drv->bdrv_snapshot_goto) - return drv->bdrv_snapshot_goto(bs, snapshot_id); - - if (bs->file) { - drv->bdrv_close(bs); - ret = bdrv_snapshot_goto(bs->file, snapshot_id); - open_ret = drv->bdrv_open(bs, NULL, bs->open_flags); - if (open_ret < 0) { - bdrv_delete(bs->file); - bs->drv = NULL; - return open_ret; - } - return ret; - } - - return -ENOTSUP; -} - -int bdrv_snapshot_delete(BlockDriverState *bs, const char *snapshot_id) -{ - BlockDriver *drv = bs->drv; - if (!drv) - return -ENOMEDIUM; - if (drv->bdrv_snapshot_delete) - return drv->bdrv_snapshot_delete(bs, snapshot_id); - if (bs->file) - return bdrv_snapshot_delete(bs->file, snapshot_id); - return -ENOTSUP; -} - -int bdrv_snapshot_list(BlockDriverState *bs, - QEMUSnapshotInfo **psn_info) -{ - BlockDriver *drv = bs->drv; - if (!drv) - return -ENOMEDIUM; - if (drv->bdrv_snapshot_list) - return drv->bdrv_snapshot_list(bs, psn_info); - if (bs->file) - return bdrv_snapshot_list(bs->file, psn_info); - return -ENOTSUP; -} - -int bdrv_snapshot_load_tmp(BlockDriverState *bs, - const char *snapshot_name) -{ - BlockDriver *drv = bs->drv; - if (!drv) { - return -ENOMEDIUM; - } - if (!bs->read_only) { - return -EINVAL; - } - if (drv->bdrv_snapshot_load_tmp) { - return drv->bdrv_snapshot_load_tmp(bs, snapshot_name); - } - return -ENOTSUP; -} - /* backing_file can either be relative, or absolute, or a protocol. If it is * relative, it must be relative to the chain. So, passing in bs->filename * from a BDS as backing_file should not be done, as that may be relative to diff --git a/block/Makefile.objs b/block/Makefile.objs index 5f0358a3d7..8670999dac 100644 --- a/block/Makefile.objs +++ b/block/Makefile.objs @@ -4,6 +4,7 @@ block-obj-y += qed.o qed-gencb.o qed-l2-cache.o qed-table.o qed-cluster.o block-obj-y += qed-check.o block-obj-y += vhdx.o block-obj-y += parallels.o blkdebug.o blkverify.o +block-obj-y += snapshot.o block-obj-$(CONFIG_WIN32) += raw-win32.o win32-aio.o block-obj-$(CONFIG_POSIX) += raw-posix.o block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o diff --git a/block/snapshot.c b/block/snapshot.c new file mode 100644 index 0000000000..6c6d9deea1 --- /dev/null +++ b/block/snapshot.c @@ -0,0 +1,157 @@ +/* + * Block layer snapshot related functions + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "block/snapshot.h" +#include "block/block_int.h" + +int bdrv_snapshot_find(BlockDriverState *bs, QEMUSnapshotInfo *sn_info, + const char *name) +{ + QEMUSnapshotInfo *sn_tab, *sn; + int nb_sns, i, ret; + + ret = -ENOENT; + nb_sns = bdrv_snapshot_list(bs, &sn_tab); + if (nb_sns < 0) { + return ret; + } + for (i = 0; i < nb_sns; i++) { + sn = &sn_tab[i]; + if (!strcmp(sn->id_str, name) || !strcmp(sn->name, name)) { + *sn_info = *sn; + ret = 0; + break; + } + } + g_free(sn_tab); + return ret; +} + +int bdrv_can_snapshot(BlockDriverState *bs) +{ + BlockDriver *drv = bs->drv; + if (!drv || !bdrv_is_inserted(bs) || bdrv_is_read_only(bs)) { + return 0; + } + + if (!drv->bdrv_snapshot_create) { + if (bs->file != NULL) { + return bdrv_can_snapshot(bs->file); + } + return 0; + } + + return 1; +} + +int bdrv_snapshot_create(BlockDriverState *bs, + QEMUSnapshotInfo *sn_info) +{ + BlockDriver *drv = bs->drv; + if (!drv) { + return -ENOMEDIUM; + } + if (drv->bdrv_snapshot_create) { + return drv->bdrv_snapshot_create(bs, sn_info); + } + if (bs->file) { + return bdrv_snapshot_create(bs->file, sn_info); + } + return -ENOTSUP; +} + +int bdrv_snapshot_goto(BlockDriverState *bs, + const char *snapshot_id) +{ + BlockDriver *drv = bs->drv; + int ret, open_ret; + + if (!drv) { + return -ENOMEDIUM; + } + if (drv->bdrv_snapshot_goto) { + return drv->bdrv_snapshot_goto(bs, snapshot_id); + } + + if (bs->file) { + drv->bdrv_close(bs); + ret = bdrv_snapshot_goto(bs->file, snapshot_id); + open_ret = drv->bdrv_open(bs, NULL, bs->open_flags); + if (open_ret < 0) { + bdrv_delete(bs->file); + bs->drv = NULL; + return open_ret; + } + return ret; + } + + return -ENOTSUP; +} + +int bdrv_snapshot_delete(BlockDriverState *bs, const char *snapshot_id) +{ + BlockDriver *drv = bs->drv; + if (!drv) { + return -ENOMEDIUM; + } + if (drv->bdrv_snapshot_delete) { + return drv->bdrv_snapshot_delete(bs, snapshot_id); + } + if (bs->file) { + return bdrv_snapshot_delete(bs->file, snapshot_id); + } + return -ENOTSUP; +} + +int bdrv_snapshot_list(BlockDriverState *bs, + QEMUSnapshotInfo **psn_info) +{ + BlockDriver *drv = bs->drv; + if (!drv) { + return -ENOMEDIUM; + } + if (drv->bdrv_snapshot_list) { + return drv->bdrv_snapshot_list(bs, psn_info); + } + if (bs->file) { + return bdrv_snapshot_list(bs->file, psn_info); + } + return -ENOTSUP; +} + +int bdrv_snapshot_load_tmp(BlockDriverState *bs, + const char *snapshot_name) +{ + BlockDriver *drv = bs->drv; + if (!drv) { + return -ENOMEDIUM; + } + if (!bs->read_only) { + return -EINVAL; + } + if (drv->bdrv_snapshot_load_tmp) { + return drv->bdrv_snapshot_load_tmp(bs, snapshot_name); + } + return -ENOTSUP; +} diff --git a/include/block/block.h b/include/block/block.h index 896553658f..a50a75f2a6 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -7,6 +7,11 @@ #include "block/coroutine.h" #include "qapi/qmp/qobject.h" #include "qapi-types.h" +/* + * snapshot.h is needed since bdrv_snapshot_dump(), it can be removed when the + * function is moved to other file. + */ +#include "block/snapshot.h" /* block.c */ typedef struct BlockDriver BlockDriver; @@ -27,17 +32,6 @@ typedef struct BlockFragInfo { uint64_t compressed_clusters; } BlockFragInfo; -typedef struct QEMUSnapshotInfo { - char id_str[128]; /* unique snapshot id */ - /* the following fields are informative. They are not needed for - the consistency of the snapshot */ - char name[256]; /* user chosen name */ - uint64_t vm_state_size; /* VM state info size */ - uint32_t date_sec; /* UTC date of the snapshot */ - uint32_t date_nsec; - uint64_t vm_clock_nsec; /* VM clock relative to boot */ -} QEMUSnapshotInfo; - /* Callbacks for block device models */ typedef struct BlockDevOps { /* @@ -331,17 +325,7 @@ void bdrv_get_full_backing_filename(BlockDriverState *bs, char *dest, size_t sz); BlockInfo *bdrv_query_info(BlockDriverState *s); BlockStats *bdrv_query_stats(const BlockDriverState *bs); -int bdrv_can_snapshot(BlockDriverState *bs); int bdrv_is_snapshot(BlockDriverState *bs); -int bdrv_snapshot_create(BlockDriverState *bs, - QEMUSnapshotInfo *sn_info); -int bdrv_snapshot_goto(BlockDriverState *bs, - const char *snapshot_id); -int bdrv_snapshot_delete(BlockDriverState *bs, const char *snapshot_id); -int bdrv_snapshot_list(BlockDriverState *bs, - QEMUSnapshotInfo **psn_info); -int bdrv_snapshot_load_tmp(BlockDriverState *bs, - const char *snapshot_name); char *bdrv_snapshot_dump(char *buf, int buf_size, QEMUSnapshotInfo *sn); char *get_human_readable_size(char *buf, int buf_size, int64_t size); diff --git a/include/block/snapshot.h b/include/block/snapshot.h new file mode 100644 index 0000000000..eaf61f0326 --- /dev/null +++ b/include/block/snapshot.h @@ -0,0 +1,53 @@ +/* + * Block layer snapshot related functions + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef SNAPSHOT_H +#define SNAPSHOT_H + +#include "qemu-common.h" + +typedef struct QEMUSnapshotInfo { + char id_str[128]; /* unique snapshot id */ + /* the following fields are informative. They are not needed for + the consistency of the snapshot */ + char name[256]; /* user chosen name */ + uint64_t vm_state_size; /* VM state info size */ + uint32_t date_sec; /* UTC date of the snapshot */ + uint32_t date_nsec; + uint64_t vm_clock_nsec; /* VM clock relative to boot */ +} QEMUSnapshotInfo; + +int bdrv_snapshot_find(BlockDriverState *bs, QEMUSnapshotInfo *sn_info, + const char *name); +int bdrv_can_snapshot(BlockDriverState *bs); +int bdrv_snapshot_create(BlockDriverState *bs, + QEMUSnapshotInfo *sn_info); +int bdrv_snapshot_goto(BlockDriverState *bs, + const char *snapshot_id); +int bdrv_snapshot_delete(BlockDriverState *bs, const char *snapshot_id); +int bdrv_snapshot_list(BlockDriverState *bs, + QEMUSnapshotInfo **psn_info); +int bdrv_snapshot_load_tmp(BlockDriverState *bs, + const char *snapshot_name); +#endif diff --git a/savevm.c b/savevm.c index b11dbf6210..916080d7dc 100644 --- a/savevm.c +++ b/savevm.c @@ -40,6 +40,7 @@ #include "trace.h" #include "qemu/bitops.h" #include "qemu/iov.h" +#include "block/snapshot.h" #define SELF_ANNOUNCE_ROUNDS 5 @@ -2273,28 +2274,6 @@ static BlockDriverState *find_vmstate_bs(void) return NULL; } -static int bdrv_snapshot_find(BlockDriverState *bs, QEMUSnapshotInfo *sn_info, - const char *name) -{ - QEMUSnapshotInfo *sn_tab, *sn; - int nb_sns, i, ret; - - ret = -ENOENT; - nb_sns = bdrv_snapshot_list(bs, &sn_tab); - if (nb_sns < 0) - return ret; - for(i = 0; i < nb_sns; i++) { - sn = &sn_tab[i]; - if (!strcmp(sn->id_str, name) || !strcmp(sn->name, name)) { - *sn_info = *sn; - ret = 0; - break; - } - } - g_free(sn_tab); - return ret; -} - /* * Deletes snapshots of a given name in all opened images. */ -- cgit v1.2.1 From f364ec65b56b69c55b674cb6560aa1fbbea9e013 Mon Sep 17 00:00:00 2001 From: Wenchao Xia Date: Sat, 25 May 2013 11:09:44 +0800 Subject: block: move qmp and info dump related code to block/qapi.c This patch is a pure code move patch, except following modification: 1 get_human_readable_size() is changed to static function. 2 dump_human_image_info() is renamed to bdrv_image_info_dump(). 3 in qmp_query_block() and qmp_query_blockstats, use bdrv_next(bs) instead of direct traverse of global array 'bdrv_states'. 4 collect_snapshots() and collect_image_info() are renamed, unused parameter *fmt in collect_image_info() is removed. 5 code style fix. To avoid conflict and tip better, macro in header file is BLOCK_QAPI_H instead of QAPI_H. Now block.h and snapshot.h are at the same level in include path, block_int.h and qapi.h will both include them. Signed-off-by: Wenchao Xia Reviewed-by: Eric Blake Signed-off-by: Kevin Wolf --- block.c | 185 ------------------------ block/Makefile.objs | 2 +- block/qapi.c | 360 ++++++++++++++++++++++++++++++++++++++++++++++ include/block/block.h | 9 -- include/block/block_int.h | 1 + include/block/qapi.h | 41 ++++++ qemu-img.c | 156 +------------------- savevm.c | 1 + 8 files changed, 408 insertions(+), 347 deletions(-) create mode 100644 block/qapi.c create mode 100644 include/block/qapi.h diff --git a/block.c b/block.c index 6212aae534..3f616de974 100644 --- a/block.c +++ b/block.c @@ -3100,128 +3100,6 @@ int bdrv_is_allocated_above(BlockDriverState *top, BlockDriverState *base, return data.ret; } -BlockInfo *bdrv_query_info(BlockDriverState *bs) -{ - BlockInfo *info = g_malloc0(sizeof(*info)); - info->device = g_strdup(bs->device_name); - info->type = g_strdup("unknown"); - info->locked = bdrv_dev_is_medium_locked(bs); - info->removable = bdrv_dev_has_removable_media(bs); - - if (bdrv_dev_has_removable_media(bs)) { - info->has_tray_open = true; - info->tray_open = bdrv_dev_is_tray_open(bs); - } - - if (bdrv_iostatus_is_enabled(bs)) { - info->has_io_status = true; - info->io_status = bs->iostatus; - } - - if (bs->dirty_bitmap) { - info->has_dirty = true; - info->dirty = g_malloc0(sizeof(*info->dirty)); - info->dirty->count = bdrv_get_dirty_count(bs) * BDRV_SECTOR_SIZE; - info->dirty->granularity = - ((int64_t) BDRV_SECTOR_SIZE << hbitmap_granularity(bs->dirty_bitmap)); - } - - if (bs->drv) { - info->has_inserted = true; - info->inserted = g_malloc0(sizeof(*info->inserted)); - info->inserted->file = g_strdup(bs->filename); - info->inserted->ro = bs->read_only; - info->inserted->drv = g_strdup(bs->drv->format_name); - info->inserted->encrypted = bs->encrypted; - info->inserted->encryption_key_missing = bdrv_key_required(bs); - - if (bs->backing_file[0]) { - info->inserted->has_backing_file = true; - info->inserted->backing_file = g_strdup(bs->backing_file); - } - - info->inserted->backing_file_depth = bdrv_get_backing_file_depth(bs); - - if (bs->io_limits_enabled) { - info->inserted->bps = - bs->io_limits.bps[BLOCK_IO_LIMIT_TOTAL]; - info->inserted->bps_rd = - bs->io_limits.bps[BLOCK_IO_LIMIT_READ]; - info->inserted->bps_wr = - bs->io_limits.bps[BLOCK_IO_LIMIT_WRITE]; - info->inserted->iops = - bs->io_limits.iops[BLOCK_IO_LIMIT_TOTAL]; - info->inserted->iops_rd = - bs->io_limits.iops[BLOCK_IO_LIMIT_READ]; - info->inserted->iops_wr = - bs->io_limits.iops[BLOCK_IO_LIMIT_WRITE]; - } - } - return info; -} - -BlockInfoList *qmp_query_block(Error **errp) -{ - BlockInfoList *head = NULL, **p_next = &head; - BlockDriverState *bs; - - QTAILQ_FOREACH(bs, &bdrv_states, list) { - BlockInfoList *info = g_malloc0(sizeof(*info)); - info->value = bdrv_query_info(bs); - - *p_next = info; - p_next = &info->next; - } - - return head; -} - -BlockStats *bdrv_query_stats(const BlockDriverState *bs) -{ - BlockStats *s; - - s = g_malloc0(sizeof(*s)); - - if (bs->device_name[0]) { - s->has_device = true; - s->device = g_strdup(bs->device_name); - } - - s->stats = g_malloc0(sizeof(*s->stats)); - s->stats->rd_bytes = bs->nr_bytes[BDRV_ACCT_READ]; - s->stats->wr_bytes = bs->nr_bytes[BDRV_ACCT_WRITE]; - s->stats->rd_operations = bs->nr_ops[BDRV_ACCT_READ]; - s->stats->wr_operations = bs->nr_ops[BDRV_ACCT_WRITE]; - s->stats->wr_highest_offset = bs->wr_highest_sector * BDRV_SECTOR_SIZE; - s->stats->flush_operations = bs->nr_ops[BDRV_ACCT_FLUSH]; - s->stats->wr_total_time_ns = bs->total_time_ns[BDRV_ACCT_WRITE]; - s->stats->rd_total_time_ns = bs->total_time_ns[BDRV_ACCT_READ]; - s->stats->flush_total_time_ns = bs->total_time_ns[BDRV_ACCT_FLUSH]; - - if (bs->file) { - s->has_parent = true; - s->parent = bdrv_query_stats(bs->file); - } - - return s; -} - -BlockStatsList *qmp_query_blockstats(Error **errp) -{ - BlockStatsList *head = NULL, **p_next = &head; - BlockDriverState *bs; - - QTAILQ_FOREACH(bs, &bdrv_states, list) { - BlockStatsList *info = g_malloc0(sizeof(*info)); - info->value = bdrv_query_stats(bs); - - *p_next = info; - p_next = &info->next; - } - - return head; -} - const char *bdrv_get_encrypted_filename(BlockDriverState *bs) { if (bs->backing_hd && bs->backing_hd->encrypted) @@ -3457,69 +3335,6 @@ BlockDriverState *bdrv_find_base(BlockDriverState *bs) return curr_bs; } -#define NB_SUFFIXES 4 - -char *get_human_readable_size(char *buf, int buf_size, int64_t size) -{ - static const char suffixes[NB_SUFFIXES] = "KMGT"; - int64_t base; - int i; - - if (size <= 999) { - snprintf(buf, buf_size, "%" PRId64, size); - } else { - base = 1024; - for(i = 0; i < NB_SUFFIXES; i++) { - if (size < (10 * base)) { - snprintf(buf, buf_size, "%0.1f%c", - (double)size / base, - suffixes[i]); - break; - } else if (size < (1000 * base) || i == (NB_SUFFIXES - 1)) { - snprintf(buf, buf_size, "%" PRId64 "%c", - ((size + (base >> 1)) / base), - suffixes[i]); - break; - } - base = base * 1024; - } - } - return buf; -} - -char *bdrv_snapshot_dump(char *buf, int buf_size, QEMUSnapshotInfo *sn) -{ - char buf1[128], date_buf[128], clock_buf[128]; - struct tm tm; - time_t ti; - int64_t secs; - - if (!sn) { - snprintf(buf, buf_size, - "%-10s%-20s%7s%20s%15s", - "ID", "TAG", "VM SIZE", "DATE", "VM CLOCK"); - } else { - ti = sn->date_sec; - localtime_r(&ti, &tm); - strftime(date_buf, sizeof(date_buf), - "%Y-%m-%d %H:%M:%S", &tm); - secs = sn->vm_clock_nsec / 1000000000; - snprintf(clock_buf, sizeof(clock_buf), - "%02d:%02d:%02d.%03d", - (int)(secs / 3600), - (int)((secs / 60) % 60), - (int)(secs % 60), - (int)((sn->vm_clock_nsec / 1000000) % 1000)); - snprintf(buf, buf_size, - "%-10s%-20s%7s%20s%15s", - sn->id_str, sn->name, - get_human_readable_size(buf1, sizeof(buf1), sn->vm_state_size), - date_buf, - clock_buf); - } - return buf; -} - /**************************************************************/ /* async I/Os */ diff --git a/block/Makefile.objs b/block/Makefile.objs index 8670999dac..2981654846 100644 --- a/block/Makefile.objs +++ b/block/Makefile.objs @@ -4,7 +4,7 @@ block-obj-y += qed.o qed-gencb.o qed-l2-cache.o qed-table.o qed-cluster.o block-obj-y += qed-check.o block-obj-y += vhdx.o block-obj-y += parallels.o blkdebug.o blkverify.o -block-obj-y += snapshot.o +block-obj-y += snapshot.o qapi.o block-obj-$(CONFIG_WIN32) += raw-win32.o win32-aio.o block-obj-$(CONFIG_POSIX) += raw-posix.o block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o diff --git a/block/qapi.c b/block/qapi.c new file mode 100644 index 0000000000..155e77ef10 --- /dev/null +++ b/block/qapi.c @@ -0,0 +1,360 @@ +/* + * Block layer qmp and info dump related functions + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "block/qapi.h" +#include "block/block_int.h" +#include "qmp-commands.h" + +void bdrv_collect_snapshots(BlockDriverState *bs , ImageInfo *info) +{ + int i, sn_count; + QEMUSnapshotInfo *sn_tab = NULL; + SnapshotInfoList *info_list, *cur_item = NULL; + sn_count = bdrv_snapshot_list(bs, &sn_tab); + + for (i = 0; i < sn_count; i++) { + info->has_snapshots = true; + info_list = g_new0(SnapshotInfoList, 1); + + info_list->value = g_new0(SnapshotInfo, 1); + info_list->value->id = g_strdup(sn_tab[i].id_str); + info_list->value->name = g_strdup(sn_tab[i].name); + info_list->value->vm_state_size = sn_tab[i].vm_state_size; + info_list->value->date_sec = sn_tab[i].date_sec; + info_list->value->date_nsec = sn_tab[i].date_nsec; + info_list->value->vm_clock_sec = sn_tab[i].vm_clock_nsec / 1000000000; + info_list->value->vm_clock_nsec = sn_tab[i].vm_clock_nsec % 1000000000; + + /* XXX: waiting for the qapi to support qemu-queue.h types */ + if (!cur_item) { + info->snapshots = cur_item = info_list; + } else { + cur_item->next = info_list; + cur_item = info_list; + } + + } + + g_free(sn_tab); +} + +void bdrv_collect_image_info(BlockDriverState *bs, + ImageInfo *info, + const char *filename) +{ + uint64_t total_sectors; + char backing_filename[1024]; + char backing_filename2[1024]; + BlockDriverInfo bdi; + + bdrv_get_geometry(bs, &total_sectors); + + info->filename = g_strdup(filename); + info->format = g_strdup(bdrv_get_format_name(bs)); + info->virtual_size = total_sectors * 512; + info->actual_size = bdrv_get_allocated_file_size(bs); + info->has_actual_size = info->actual_size >= 0; + if (bdrv_is_encrypted(bs)) { + info->encrypted = true; + info->has_encrypted = true; + } + if (bdrv_get_info(bs, &bdi) >= 0) { + if (bdi.cluster_size != 0) { + info->cluster_size = bdi.cluster_size; + info->has_cluster_size = true; + } + info->dirty_flag = bdi.is_dirty; + info->has_dirty_flag = true; + } + bdrv_get_backing_filename(bs, backing_filename, sizeof(backing_filename)); + if (backing_filename[0] != '\0') { + info->backing_filename = g_strdup(backing_filename); + info->has_backing_filename = true; + bdrv_get_full_backing_filename(bs, backing_filename2, + sizeof(backing_filename2)); + + if (strcmp(backing_filename, backing_filename2) != 0) { + info->full_backing_filename = + g_strdup(backing_filename2); + info->has_full_backing_filename = true; + } + + if (bs->backing_format[0]) { + info->backing_filename_format = g_strdup(bs->backing_format); + info->has_backing_filename_format = true; + } + } +} + +BlockInfo *bdrv_query_info(BlockDriverState *bs) +{ + BlockInfo *info = g_malloc0(sizeof(*info)); + info->device = g_strdup(bs->device_name); + info->type = g_strdup("unknown"); + info->locked = bdrv_dev_is_medium_locked(bs); + info->removable = bdrv_dev_has_removable_media(bs); + + if (bdrv_dev_has_removable_media(bs)) { + info->has_tray_open = true; + info->tray_open = bdrv_dev_is_tray_open(bs); + } + + if (bdrv_iostatus_is_enabled(bs)) { + info->has_io_status = true; + info->io_status = bs->iostatus; + } + + if (bs->dirty_bitmap) { + info->has_dirty = true; + info->dirty = g_malloc0(sizeof(*info->dirty)); + info->dirty->count = bdrv_get_dirty_count(bs) * BDRV_SECTOR_SIZE; + info->dirty->granularity = + ((int64_t) BDRV_SECTOR_SIZE << hbitmap_granularity(bs->dirty_bitmap)); + } + + if (bs->drv) { + info->has_inserted = true; + info->inserted = g_malloc0(sizeof(*info->inserted)); + info->inserted->file = g_strdup(bs->filename); + info->inserted->ro = bs->read_only; + info->inserted->drv = g_strdup(bs->drv->format_name); + info->inserted->encrypted = bs->encrypted; + info->inserted->encryption_key_missing = bdrv_key_required(bs); + + if (bs->backing_file[0]) { + info->inserted->has_backing_file = true; + info->inserted->backing_file = g_strdup(bs->backing_file); + } + + info->inserted->backing_file_depth = bdrv_get_backing_file_depth(bs); + + if (bs->io_limits_enabled) { + info->inserted->bps = + bs->io_limits.bps[BLOCK_IO_LIMIT_TOTAL]; + info->inserted->bps_rd = + bs->io_limits.bps[BLOCK_IO_LIMIT_READ]; + info->inserted->bps_wr = + bs->io_limits.bps[BLOCK_IO_LIMIT_WRITE]; + info->inserted->iops = + bs->io_limits.iops[BLOCK_IO_LIMIT_TOTAL]; + info->inserted->iops_rd = + bs->io_limits.iops[BLOCK_IO_LIMIT_READ]; + info->inserted->iops_wr = + bs->io_limits.iops[BLOCK_IO_LIMIT_WRITE]; + } + } + return info; +} + +BlockStats *bdrv_query_stats(const BlockDriverState *bs) +{ + BlockStats *s; + + s = g_malloc0(sizeof(*s)); + + if (bs->device_name[0]) { + s->has_device = true; + s->device = g_strdup(bs->device_name); + } + + s->stats = g_malloc0(sizeof(*s->stats)); + s->stats->rd_bytes = bs->nr_bytes[BDRV_ACCT_READ]; + s->stats->wr_bytes = bs->nr_bytes[BDRV_ACCT_WRITE]; + s->stats->rd_operations = bs->nr_ops[BDRV_ACCT_READ]; + s->stats->wr_operations = bs->nr_ops[BDRV_ACCT_WRITE]; + s->stats->wr_highest_offset = bs->wr_highest_sector * BDRV_SECTOR_SIZE; + s->stats->flush_operations = bs->nr_ops[BDRV_ACCT_FLUSH]; + s->stats->wr_total_time_ns = bs->total_time_ns[BDRV_ACCT_WRITE]; + s->stats->rd_total_time_ns = bs->total_time_ns[BDRV_ACCT_READ]; + s->stats->flush_total_time_ns = bs->total_time_ns[BDRV_ACCT_FLUSH]; + + if (bs->file) { + s->has_parent = true; + s->parent = bdrv_query_stats(bs->file); + } + + return s; +} + +BlockInfoList *qmp_query_block(Error **errp) +{ + BlockInfoList *head = NULL, **p_next = &head; + BlockDriverState *bs = NULL; + + while ((bs = bdrv_next(bs))) { + BlockInfoList *info = g_malloc0(sizeof(*info)); + info->value = bdrv_query_info(bs); + + *p_next = info; + p_next = &info->next; + } + + return head; +} + +BlockStatsList *qmp_query_blockstats(Error **errp) +{ + BlockStatsList *head = NULL, **p_next = &head; + BlockDriverState *bs = NULL; + + while ((bs = bdrv_next(bs))) { + BlockStatsList *info = g_malloc0(sizeof(*info)); + info->value = bdrv_query_stats(bs); + + *p_next = info; + p_next = &info->next; + } + + return head; +} + +#define NB_SUFFIXES 4 + +static char *get_human_readable_size(char *buf, int buf_size, int64_t size) +{ + static const char suffixes[NB_SUFFIXES] = "KMGT"; + int64_t base; + int i; + + if (size <= 999) { + snprintf(buf, buf_size, "%" PRId64, size); + } else { + base = 1024; + for (i = 0; i < NB_SUFFIXES; i++) { + if (size < (10 * base)) { + snprintf(buf, buf_size, "%0.1f%c", + (double)size / base, + suffixes[i]); + break; + } else if (size < (1000 * base) || i == (NB_SUFFIXES - 1)) { + snprintf(buf, buf_size, "%" PRId64 "%c", + ((size + (base >> 1)) / base), + suffixes[i]); + break; + } + base = base * 1024; + } + } + return buf; +} + +char *bdrv_snapshot_dump(char *buf, int buf_size, QEMUSnapshotInfo *sn) +{ + char buf1[128], date_buf[128], clock_buf[128]; + struct tm tm; + time_t ti; + int64_t secs; + + if (!sn) { + snprintf(buf, buf_size, + "%-10s%-20s%7s%20s%15s", + "ID", "TAG", "VM SIZE", "DATE", "VM CLOCK"); + } else { + ti = sn->date_sec; + localtime_r(&ti, &tm); + strftime(date_buf, sizeof(date_buf), + "%Y-%m-%d %H:%M:%S", &tm); + secs = sn->vm_clock_nsec / 1000000000; + snprintf(clock_buf, sizeof(clock_buf), + "%02d:%02d:%02d.%03d", + (int)(secs / 3600), + (int)((secs / 60) % 60), + (int)(secs % 60), + (int)((sn->vm_clock_nsec / 1000000) % 1000)); + snprintf(buf, buf_size, + "%-10s%-20s%7s%20s%15s", + sn->id_str, sn->name, + get_human_readable_size(buf1, sizeof(buf1), sn->vm_state_size), + date_buf, + clock_buf); + } + return buf; +} + +void bdrv_image_info_dump(ImageInfo *info) +{ + char size_buf[128], dsize_buf[128]; + if (!info->has_actual_size) { + snprintf(dsize_buf, sizeof(dsize_buf), "unavailable"); + } else { + get_human_readable_size(dsize_buf, sizeof(dsize_buf), + info->actual_size); + } + get_human_readable_size(size_buf, sizeof(size_buf), info->virtual_size); + printf("image: %s\n" + "file format: %s\n" + "virtual size: %s (%" PRId64 " bytes)\n" + "disk size: %s\n", + info->filename, info->format, size_buf, + info->virtual_size, + dsize_buf); + + if (info->has_encrypted && info->encrypted) { + printf("encrypted: yes\n"); + } + + if (info->has_cluster_size) { + printf("cluster_size: %" PRId64 "\n", info->cluster_size); + } + + if (info->has_dirty_flag && info->dirty_flag) { + printf("cleanly shut down: no\n"); + } + + if (info->has_backing_filename) { + printf("backing file: %s", info->backing_filename); + if (info->has_full_backing_filename) { + printf(" (actual path: %s)", info->full_backing_filename); + } + putchar('\n'); + if (info->has_backing_filename_format) { + printf("backing file format: %s\n", info->backing_filename_format); + } + } + + if (info->has_snapshots) { + SnapshotInfoList *elem; + char buf[256]; + + printf("Snapshot list:\n"); + printf("%s\n", bdrv_snapshot_dump(buf, sizeof(buf), NULL)); + + /* Ideally bdrv_snapshot_dump() would operate on SnapshotInfoList but + * we convert to the block layer's native QEMUSnapshotInfo for now. + */ + for (elem = info->snapshots; elem; elem = elem->next) { + QEMUSnapshotInfo sn = { + .vm_state_size = elem->value->vm_state_size, + .date_sec = elem->value->date_sec, + .date_nsec = elem->value->date_nsec, + .vm_clock_nsec = elem->value->vm_clock_sec * 1000000000ULL + + elem->value->vm_clock_nsec, + }; + + pstrcpy(sn.id_str, sizeof(sn.id_str), elem->value->id); + pstrcpy(sn.name, sizeof(sn.name), elem->value->name); + printf("%s\n", bdrv_snapshot_dump(buf, sizeof(buf), &sn)); + } + } +} diff --git a/include/block/block.h b/include/block/block.h index a50a75f2a6..dc5b388d87 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -7,11 +7,6 @@ #include "block/coroutine.h" #include "qapi/qmp/qobject.h" #include "qapi-types.h" -/* - * snapshot.h is needed since bdrv_snapshot_dump(), it can be removed when the - * function is moved to other file. - */ -#include "block/snapshot.h" /* block.c */ typedef struct BlockDriver BlockDriver; @@ -323,12 +318,8 @@ void bdrv_get_backing_filename(BlockDriverState *bs, char *filename, int filename_size); void bdrv_get_full_backing_filename(BlockDriverState *bs, char *dest, size_t sz); -BlockInfo *bdrv_query_info(BlockDriverState *s); -BlockStats *bdrv_query_stats(const BlockDriverState *bs); int bdrv_is_snapshot(BlockDriverState *bs); -char *bdrv_snapshot_dump(char *buf, int buf_size, QEMUSnapshotInfo *sn); -char *get_human_readable_size(char *buf, int buf_size, int64_t size); int path_is_absolute(const char *path); void path_combine(char *dest, int dest_size, const char *base_path, diff --git a/include/block/block_int.h b/include/block/block_int.h index 6078dd389f..ba52247c1a 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -33,6 +33,7 @@ #include "qapi/qmp/qerror.h" #include "monitor/monitor.h" #include "qemu/hbitmap.h" +#include "block/snapshot.h" #define BLOCK_FLAG_ENCRYPT 1 #define BLOCK_FLAG_COMPAT6 4 diff --git a/include/block/qapi.h b/include/block/qapi.h new file mode 100644 index 0000000000..55d1848de9 --- /dev/null +++ b/include/block/qapi.h @@ -0,0 +1,41 @@ +/* + * Block layer qmp and info dump related functions + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef BLOCK_QAPI_H +#define BLOCK_QAPI_H + +#include "qapi-types.h" +#include "block/block.h" +#include "block/snapshot.h" + +void bdrv_collect_snapshots(BlockDriverState *bs , ImageInfo *info); +void bdrv_collect_image_info(BlockDriverState *bs, + ImageInfo *info, + const char *filename); +BlockInfo *bdrv_query_info(BlockDriverState *s); +BlockStats *bdrv_query_stats(const BlockDriverState *bs); + +char *bdrv_snapshot_dump(char *buf, int buf_size, QEMUSnapshotInfo *sn); +void bdrv_image_info_dump(ImageInfo *info); +#endif diff --git a/qemu-img.c b/qemu-img.c index cd096a1361..5d1e480fcb 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -30,6 +30,7 @@ #include "qemu/osdep.h" #include "sysemu/sysemu.h" #include "block/block_int.h" +#include "block/qapi.h" #include #include #include @@ -1584,39 +1585,6 @@ static void dump_json_image_info_list(ImageInfoList *list) QDECREF(str); } -static void collect_snapshots(BlockDriverState *bs , ImageInfo *info) -{ - int i, sn_count; - QEMUSnapshotInfo *sn_tab = NULL; - SnapshotInfoList *info_list, *cur_item = NULL; - sn_count = bdrv_snapshot_list(bs, &sn_tab); - - for (i = 0; i < sn_count; i++) { - info->has_snapshots = true; - info_list = g_new0(SnapshotInfoList, 1); - - info_list->value = g_new0(SnapshotInfo, 1); - info_list->value->id = g_strdup(sn_tab[i].id_str); - info_list->value->name = g_strdup(sn_tab[i].name); - info_list->value->vm_state_size = sn_tab[i].vm_state_size; - info_list->value->date_sec = sn_tab[i].date_sec; - info_list->value->date_nsec = sn_tab[i].date_nsec; - info_list->value->vm_clock_sec = sn_tab[i].vm_clock_nsec / 1000000000; - info_list->value->vm_clock_nsec = sn_tab[i].vm_clock_nsec % 1000000000; - - /* XXX: waiting for the qapi to support qemu-queue.h types */ - if (!cur_item) { - info->snapshots = cur_item = info_list; - } else { - cur_item->next = info_list; - cur_item = info_list; - } - - } - - g_free(sn_tab); -} - static void dump_json_image_info(ImageInfo *info) { Error *errp = NULL; @@ -1634,122 +1602,6 @@ static void dump_json_image_info(ImageInfo *info) QDECREF(str); } -static void collect_image_info(BlockDriverState *bs, - ImageInfo *info, - const char *filename, - const char *fmt) -{ - uint64_t total_sectors; - char backing_filename[1024]; - char backing_filename2[1024]; - BlockDriverInfo bdi; - - bdrv_get_geometry(bs, &total_sectors); - - info->filename = g_strdup(filename); - info->format = g_strdup(bdrv_get_format_name(bs)); - info->virtual_size = total_sectors * 512; - info->actual_size = bdrv_get_allocated_file_size(bs); - info->has_actual_size = info->actual_size >= 0; - if (bdrv_is_encrypted(bs)) { - info->encrypted = true; - info->has_encrypted = true; - } - if (bdrv_get_info(bs, &bdi) >= 0) { - if (bdi.cluster_size != 0) { - info->cluster_size = bdi.cluster_size; - info->has_cluster_size = true; - } - info->dirty_flag = bdi.is_dirty; - info->has_dirty_flag = true; - } - bdrv_get_backing_filename(bs, backing_filename, sizeof(backing_filename)); - if (backing_filename[0] != '\0') { - info->backing_filename = g_strdup(backing_filename); - info->has_backing_filename = true; - bdrv_get_full_backing_filename(bs, backing_filename2, - sizeof(backing_filename2)); - - if (strcmp(backing_filename, backing_filename2) != 0) { - info->full_backing_filename = - g_strdup(backing_filename2); - info->has_full_backing_filename = true; - } - - if (bs->backing_format[0]) { - info->backing_filename_format = g_strdup(bs->backing_format); - info->has_backing_filename_format = true; - } - } -} - -static void dump_human_image_info(ImageInfo *info) -{ - char size_buf[128], dsize_buf[128]; - if (!info->has_actual_size) { - snprintf(dsize_buf, sizeof(dsize_buf), "unavailable"); - } else { - get_human_readable_size(dsize_buf, sizeof(dsize_buf), - info->actual_size); - } - get_human_readable_size(size_buf, sizeof(size_buf), info->virtual_size); - printf("image: %s\n" - "file format: %s\n" - "virtual size: %s (%" PRId64 " bytes)\n" - "disk size: %s\n", - info->filename, info->format, size_buf, - info->virtual_size, - dsize_buf); - - if (info->has_encrypted && info->encrypted) { - printf("encrypted: yes\n"); - } - - if (info->has_cluster_size) { - printf("cluster_size: %" PRId64 "\n", info->cluster_size); - } - - if (info->has_dirty_flag && info->dirty_flag) { - printf("cleanly shut down: no\n"); - } - - if (info->has_backing_filename) { - printf("backing file: %s", info->backing_filename); - if (info->has_full_backing_filename) { - printf(" (actual path: %s)", info->full_backing_filename); - } - putchar('\n'); - if (info->has_backing_filename_format) { - printf("backing file format: %s\n", info->backing_filename_format); - } - } - - if (info->has_snapshots) { - SnapshotInfoList *elem; - char buf[256]; - - printf("Snapshot list:\n"); - printf("%s\n", bdrv_snapshot_dump(buf, sizeof(buf), NULL)); - - /* Ideally bdrv_snapshot_dump() would operate on SnapshotInfoList but - * we convert to the block layer's native QEMUSnapshotInfo for now. - */ - for (elem = info->snapshots; elem; elem = elem->next) { - QEMUSnapshotInfo sn = { - .vm_state_size = elem->value->vm_state_size, - .date_sec = elem->value->date_sec, - .date_nsec = elem->value->date_nsec, - .vm_clock_nsec = elem->value->vm_clock_sec * 1000000000ULL + - elem->value->vm_clock_nsec, - }; - - pstrcpy(sn.id_str, sizeof(sn.id_str), elem->value->id); - pstrcpy(sn.name, sizeof(sn.name), elem->value->name); - printf("%s\n", bdrv_snapshot_dump(buf, sizeof(buf), &sn)); - } - } -} - static void dump_human_image_info_list(ImageInfoList *list) { ImageInfoList *elem; @@ -1761,7 +1613,7 @@ static void dump_human_image_info_list(ImageInfoList *list) } delim = true; - dump_human_image_info(elem->value); + bdrv_image_info_dump(elem->value); } } @@ -1811,8 +1663,8 @@ static ImageInfoList *collect_image_info_list(const char *filename, } info = g_new0(ImageInfo, 1); - collect_image_info(bs, info, filename, fmt); - collect_snapshots(bs, info); + bdrv_collect_image_info(bs, info, filename); + bdrv_collect_snapshots(bs, info); elem = g_new0(ImageInfoList, 1); elem->value = info; diff --git a/savevm.c b/savevm.c index 916080d7dc..a886514c00 100644 --- a/savevm.c +++ b/savevm.c @@ -41,6 +41,7 @@ #include "qemu/bitops.h" #include "qemu/iov.h" #include "block/snapshot.h" +#include "block/qapi.h" #define SELF_ANNOUNCE_ROUNDS 5 -- cgit v1.2.1 From 5b91704469c0f801e0219f26458356872c4145ab Mon Sep 17 00:00:00 2001 From: Wenchao Xia Date: Sat, 25 May 2013 11:09:45 +0800 Subject: block: dump snapshot and image info to specified output bdrv_snapshot_dump() and bdrv_image_info_dump() do not dump to a buffer now, some internal buffers are still used for format control, which have no chance to be truncated. As a result, these two functions have no more issue of truncation, and they can be used by both qemu and qemu-img with correct parameter specified. Signed-off-by: Wenchao Xia Signed-off-by: Kevin Wolf --- block/qapi.c | 66 ++++++++++++++++++++++++++++------------------------ include/block/qapi.h | 6 +++-- qemu-img.c | 9 +++---- savevm.c | 7 +++--- 4 files changed, 49 insertions(+), 39 deletions(-) diff --git a/block/qapi.c b/block/qapi.c index 155e77ef10..794dbf8fe8 100644 --- a/block/qapi.c +++ b/block/qapi.c @@ -259,7 +259,8 @@ static char *get_human_readable_size(char *buf, int buf_size, int64_t size) return buf; } -char *bdrv_snapshot_dump(char *buf, int buf_size, QEMUSnapshotInfo *sn) +void bdrv_snapshot_dump(fprintf_function func_fprintf, void *f, + QEMUSnapshotInfo *sn) { char buf1[128], date_buf[128], clock_buf[128]; struct tm tm; @@ -267,9 +268,9 @@ char *bdrv_snapshot_dump(char *buf, int buf_size, QEMUSnapshotInfo *sn) int64_t secs; if (!sn) { - snprintf(buf, buf_size, - "%-10s%-20s%7s%20s%15s", - "ID", "TAG", "VM SIZE", "DATE", "VM CLOCK"); + func_fprintf(f, + "%-10s%-20s%7s%20s%15s", + "ID", "TAG", "VM SIZE", "DATE", "VM CLOCK"); } else { ti = sn->date_sec; localtime_r(&ti, &tm); @@ -282,17 +283,18 @@ char *bdrv_snapshot_dump(char *buf, int buf_size, QEMUSnapshotInfo *sn) (int)((secs / 60) % 60), (int)(secs % 60), (int)((sn->vm_clock_nsec / 1000000) % 1000)); - snprintf(buf, buf_size, - "%-10s%-20s%7s%20s%15s", - sn->id_str, sn->name, - get_human_readable_size(buf1, sizeof(buf1), sn->vm_state_size), - date_buf, - clock_buf); + func_fprintf(f, + "%-10s%-20s%7s%20s%15s", + sn->id_str, sn->name, + get_human_readable_size(buf1, sizeof(buf1), + sn->vm_state_size), + date_buf, + clock_buf); } - return buf; } -void bdrv_image_info_dump(ImageInfo *info) +void bdrv_image_info_dump(fprintf_function func_fprintf, void *f, + ImageInfo *info) { char size_buf[128], dsize_buf[128]; if (!info->has_actual_size) { @@ -302,43 +304,46 @@ void bdrv_image_info_dump(ImageInfo *info) info->actual_size); } get_human_readable_size(size_buf, sizeof(size_buf), info->virtual_size); - printf("image: %s\n" - "file format: %s\n" - "virtual size: %s (%" PRId64 " bytes)\n" - "disk size: %s\n", - info->filename, info->format, size_buf, - info->virtual_size, - dsize_buf); + func_fprintf(f, + "image: %s\n" + "file format: %s\n" + "virtual size: %s (%" PRId64 " bytes)\n" + "disk size: %s\n", + info->filename, info->format, size_buf, + info->virtual_size, + dsize_buf); if (info->has_encrypted && info->encrypted) { - printf("encrypted: yes\n"); + func_fprintf(f, "encrypted: yes\n"); } if (info->has_cluster_size) { - printf("cluster_size: %" PRId64 "\n", info->cluster_size); + func_fprintf(f, "cluster_size: %" PRId64 "\n", + info->cluster_size); } if (info->has_dirty_flag && info->dirty_flag) { - printf("cleanly shut down: no\n"); + func_fprintf(f, "cleanly shut down: no\n"); } if (info->has_backing_filename) { - printf("backing file: %s", info->backing_filename); + func_fprintf(f, "backing file: %s", info->backing_filename); if (info->has_full_backing_filename) { - printf(" (actual path: %s)", info->full_backing_filename); + func_fprintf(f, " (actual path: %s)", info->full_backing_filename); } - putchar('\n'); + func_fprintf(f, "\n"); if (info->has_backing_filename_format) { - printf("backing file format: %s\n", info->backing_filename_format); + func_fprintf(f, "backing file format: %s\n", + info->backing_filename_format); } } if (info->has_snapshots) { SnapshotInfoList *elem; - char buf[256]; - printf("Snapshot list:\n"); - printf("%s\n", bdrv_snapshot_dump(buf, sizeof(buf), NULL)); + func_fprintf(f, "Snapshot list:\n"); + bdrv_snapshot_dump(func_fprintf, f, NULL); + func_fprintf(f, "\n"); /* Ideally bdrv_snapshot_dump() would operate on SnapshotInfoList but * we convert to the block layer's native QEMUSnapshotInfo for now. @@ -354,7 +359,8 @@ void bdrv_image_info_dump(ImageInfo *info) pstrcpy(sn.id_str, sizeof(sn.id_str), elem->value->id); pstrcpy(sn.name, sizeof(sn.name), elem->value->name); - printf("%s\n", bdrv_snapshot_dump(buf, sizeof(buf), &sn)); + bdrv_snapshot_dump(func_fprintf, f, &sn); + func_fprintf(f, "\n"); } } } diff --git a/include/block/qapi.h b/include/block/qapi.h index 55d1848de9..e6e568da94 100644 --- a/include/block/qapi.h +++ b/include/block/qapi.h @@ -36,6 +36,8 @@ void bdrv_collect_image_info(BlockDriverState *bs, BlockInfo *bdrv_query_info(BlockDriverState *s); BlockStats *bdrv_query_stats(const BlockDriverState *bs); -char *bdrv_snapshot_dump(char *buf, int buf_size, QEMUSnapshotInfo *sn); -void bdrv_image_info_dump(ImageInfo *info); +void bdrv_snapshot_dump(fprintf_function func_fprintf, void *f, + QEMUSnapshotInfo *sn); +void bdrv_image_info_dump(fprintf_function func_fprintf, void *f, + ImageInfo *info); #endif diff --git a/qemu-img.c b/qemu-img.c index 5d1e480fcb..82c7977353 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -1554,16 +1554,17 @@ static void dump_snapshots(BlockDriverState *bs) { QEMUSnapshotInfo *sn_tab, *sn; int nb_sns, i; - char buf[256]; nb_sns = bdrv_snapshot_list(bs, &sn_tab); if (nb_sns <= 0) return; printf("Snapshot list:\n"); - printf("%s\n", bdrv_snapshot_dump(buf, sizeof(buf), NULL)); + bdrv_snapshot_dump(fprintf, stdout, NULL); + printf("\n"); for(i = 0; i < nb_sns; i++) { sn = &sn_tab[i]; - printf("%s\n", bdrv_snapshot_dump(buf, sizeof(buf), sn)); + bdrv_snapshot_dump(fprintf, stdout, sn); + printf("\n"); } g_free(sn_tab); } @@ -1613,7 +1614,7 @@ static void dump_human_image_info_list(ImageInfoList *list) } delim = true; - bdrv_image_info_dump(elem->value); + bdrv_image_info_dump(fprintf, stdout, elem->value); } } diff --git a/savevm.c b/savevm.c index a886514c00..2ce439f7f4 100644 --- a/savevm.c +++ b/savevm.c @@ -2540,7 +2540,6 @@ void do_info_snapshots(Monitor *mon, const QDict *qdict) int nb_sns, i, ret, available; int total; int *available_snapshots; - char buf[256]; bs = find_vmstate_bs(); if (!bs) { @@ -2583,10 +2582,12 @@ void do_info_snapshots(Monitor *mon, const QDict *qdict) } if (total > 0) { - monitor_printf(mon, "%s\n", bdrv_snapshot_dump(buf, sizeof(buf), NULL)); + bdrv_snapshot_dump((fprintf_function)monitor_printf, mon, NULL); + monitor_printf(mon, "\n"); for (i = 0; i < total; i++) { sn = &sn_tab[available_snapshots[i]]; - monitor_printf(mon, "%s\n", bdrv_snapshot_dump(buf, sizeof(buf), sn)); + bdrv_snapshot_dump((fprintf_function)monitor_printf, mon, sn); + monitor_printf(mon, "\n"); } } else { monitor_printf(mon, "There is no suitable snapshot available\n"); -- cgit v1.2.1