diff options
Diffstat (limited to 'tests')
61 files changed, 1979 insertions, 311 deletions
diff --git a/tests/Makefile.include b/tests/Makefile.include index 09e5b410dc..7f487f65e7 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -140,7 +140,7 @@ check-unit-y += tests/test-base64$(EXESUF) check-unit-$(call land,$(CONFIG_BLOCK),$(if $(CONFIG_NETTLE),y,$(CONFIG_GCRYPT))) += tests/test-crypto-pbkdf$(EXESUF) check-unit-$(CONFIG_BLOCK) += tests/test-crypto-ivgen$(EXESUF) check-unit-$(CONFIG_BLOCK) += tests/test-crypto-afsplit$(EXESUF) -check-unit-$(CONFIG_BLOCK) += tests/test-crypto-xts$(EXESUF) +check-unit-$(if $(CONFIG_BLOCK),$(CONFIG_QEMU_PRIVATE_XTS)) += tests/test-crypto-xts$(EXESUF) check-unit-$(CONFIG_BLOCK) += tests/test-crypto-block$(EXESUF) check-unit-y += tests/test-logging$(EXESUF) check-unit-$(call land,$(CONFIG_BLOCK),$(CONFIG_REPLICATION)) += tests/test-replication$(EXESUF) @@ -723,6 +723,7 @@ qos-test-obj-y += tests/libqos/virtio-blk.o qos-test-obj-y += tests/libqos/virtio-mmio.o qos-test-obj-y += tests/libqos/virtio-net.o qos-test-obj-y += tests/libqos/virtio-pci.o +qos-test-obj-y += tests/libqos/virtio-pci-modern.o qos-test-obj-y += tests/libqos/virtio-rng.o qos-test-obj-y += tests/libqos/virtio-scsi.o qos-test-obj-y += tests/libqos/virtio-serial.o diff --git a/tests/acceptance/linux_ssh_mips_malta.py b/tests/acceptance/linux_ssh_mips_malta.py index 25a1df5098..aa12001942 100644 --- a/tests/acceptance/linux_ssh_mips_malta.py +++ b/tests/acceptance/linux_ssh_mips_malta.py @@ -25,15 +25,44 @@ class LinuxSSH(Test): KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 ' VM_IP = '127.0.0.1' + BASE_URL = 'https://people.debian.org/~aurel32/qemu/' IMAGE_INFO = { - 'be': {'image_url': ('https://people.debian.org/~aurel32/qemu/mips/' - 'debian_wheezy_mips_standard.qcow2'), - 'image_hash': '8987a63270df67345b2135a6b7a4885a35e392d5'}, - 'le': {'image_url': ('https://people.debian.org/~aurel32/qemu/mipsel/' - 'debian_wheezy_mipsel_standard.qcow2'), - 'image_hash': '7866764d9de3ef536ffca24c9fb9f04ffdb45802'} + 'be': {'base_url': 'mips', + 'image_name': 'debian_wheezy_mips_standard.qcow2', + 'image_hash': '8987a63270df67345b2135a6b7a4885a35e392d5', + 'kernel_hash': { + 32: '592e384a4edc16dade52a6cd5c785c637bcbc9ad', + 64: 'db6eea7de35d36c77d8c165b6bcb222e16eb91db'} + }, + 'le': {'base_url': 'mipsel', + 'image_name': 'debian_wheezy_mipsel_standard.qcow2', + 'image_hash': '7866764d9de3ef536ffca24c9fb9f04ffdb45802', + 'kernel_hash': { + 32: 'a66bea5a8adaa2cb3d36a1d4e0ccdb01be8f6c2a', + 64: '6a7f77245acf231415a0e8b725d91ed2f3487794'} + } } + CPU_INFO = { + 32: {'cpu': 'MIPS 24Kc', 'kernel_release': '3.2.0-4-4kc-malta'}, + 64: {'cpu': 'MIPS 20Kc', 'kernel_release': '3.2.0-4-5kc-malta'} + } + + def get_url(self, endianess, path=''): + qkey = {'le': 'el', 'be': ''} + return '%s/mips%s/%s' % (self.BASE_URL, qkey[endianess], path) + + def get_image_info(self, endianess): + dinfo = self.IMAGE_INFO[endianess] + image_url = self.get_url(endianess, dinfo['image_name']) + image_hash = dinfo['image_hash'] + return (image_url, image_hash) + def get_kernel_info(self, endianess, wordsize): + minfo = self.CPU_INFO[wordsize] + kernel_url = self.get_url(endianess, + 'vmlinux-%s' % minfo['kernel_release']) + kernel_hash = self.IMAGE_INFO[endianess]['kernel_hash'][wordsize] + return kernel_url, kernel_hash @skipUnless(ssh.SSH_CLIENT_BINARY, 'No SSH client available') @skipUnless(os.getenv('AVOCADO_TIMEOUT_EXPECTED'), 'Test might timeout') @@ -91,8 +120,7 @@ class LinuxSSH(Test): return stdout_lines, stderr_lines def boot_debian_wheezy_image_and_ssh_login(self, endianess, kernel_path): - image_url = self.IMAGE_INFO[endianess]['image_url'] - image_hash = self.IMAGE_INFO[endianess]['image_hash'] + image_url, image_hash = self.get_image_info(endianess) image_path = self.fetch_asset(image_url, asset_hash=image_hash) self.vm.set_machine('malta') @@ -102,7 +130,7 @@ class LinuxSSH(Test): self.vm.add_args('-no-reboot', '-kernel', kernel_path, '-append', kernel_command_line, - '-hda', image_path, + '-drive', 'file=%s,snapshot=on' % image_path, '-netdev', 'user,id=vnet,hostfwd=:127.0.0.1:0-:22', '-device', 'pcnet,netdev=vnet') self.vm.launch() @@ -127,34 +155,31 @@ class LinuxSSH(Test): else: self.fail('"%s" output does not contain "%s"' % (cmd, exp)) - def run_common_commands(self): + def run_common_commands(self, wordsize): self.ssh_command_output_contains( 'cat /proc/cpuinfo', - '24Kc') + self.CPU_INFO[wordsize]['cpu']) self.ssh_command_output_contains( 'uname -m', 'mips') self.ssh_command_output_contains( 'uname -r', - '3.2.0-4-4kc-malta') + self.CPU_INFO[wordsize]['kernel_release']) self.ssh_command_output_contains( 'cat /proc/interrupts', - 'timer') + 'XT-PIC timer') self.ssh_command_output_contains( 'cat /proc/interrupts', - 'i8042') + 'XT-PIC i8042') self.ssh_command_output_contains( 'cat /proc/interrupts', - 'serial') + 'XT-PIC serial') self.ssh_command_output_contains( 'cat /proc/interrupts', - 'ata_piix') + 'XT-PIC ata_piix') self.ssh_command_output_contains( 'cat /proc/interrupts', - 'eth0') - self.ssh_command_output_contains( - 'cat /proc/interrupts', - 'eth0') + 'XT-PIC eth0') self.ssh_command_output_contains( 'cat /proc/devices', 'input') @@ -166,13 +191,13 @@ class LinuxSSH(Test): 'fb') self.ssh_command_output_contains( 'cat /proc/ioports', - 'serial') + ' : serial') self.ssh_command_output_contains( 'cat /proc/ioports', - 'ata_piix') + ' : ata_piix') self.ssh_command_output_contains( 'cat /proc/ioports', - 'piix4_smbus') + ' : piix4_smbus') self.ssh_command_output_contains( 'lspci -d 11ab:4620', 'GT-64120') @@ -182,18 +207,21 @@ class LinuxSSH(Test): self.ssh_command_output_contains( 'cat /proc/mtd', 'YAMON') - # Empty 'Board Config' + # Empty 'Board Config' (64KB) self.ssh_command_output_contains( 'md5sum /dev/mtd2ro', '0dfbe8aa4c20b52e1b8bf3cb6cbdf193') - def check_mips_malta(self, endianess, kernel_path, uname_m): + def check_mips_malta(self, uname_m, endianess): + wordsize = 64 if '64' in uname_m else 32 + kernel_url, kernel_hash = self.get_kernel_info(endianess, wordsize) + kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) self.boot_debian_wheezy_image_and_ssh_login(endianess, kernel_path) stdout, _ = self.ssh_command('uname -a') self.assertIn(True, [uname_m + " GNU/Linux" in line for line in stdout]) - self.run_common_commands() + self.run_common_commands(wordsize) self.shutdown_via_ssh() def test_mips_malta32eb_kernel3_2_0(self): @@ -203,12 +231,7 @@ class LinuxSSH(Test): :avocado: tags=endian:big :avocado: tags=device:pcnet32 """ - kernel_url = ('https://people.debian.org/~aurel32/qemu/mips/' - 'vmlinux-3.2.0-4-4kc-malta') - kernel_hash = '592e384a4edc16dade52a6cd5c785c637bcbc9ad' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - - self.check_mips_malta('be', kernel_path, 'mips') + self.check_mips_malta('mips', 'be') def test_mips_malta32el_kernel3_2_0(self): """ @@ -217,12 +240,7 @@ class LinuxSSH(Test): :avocado: tags=endian:little :avocado: tags=device:pcnet32 """ - kernel_url = ('https://people.debian.org/~aurel32/qemu/mipsel/' - 'vmlinux-3.2.0-4-4kc-malta') - kernel_hash = 'a66bea5a8adaa2cb3d36a1d4e0ccdb01be8f6c2a' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - - self.check_mips_malta('le', kernel_path, 'mips') + self.check_mips_malta('mips', 'le') def test_mips_malta64eb_kernel3_2_0(self): """ @@ -231,11 +249,7 @@ class LinuxSSH(Test): :avocado: tags=endian:big :avocado: tags=device:pcnet32 """ - kernel_url = ('https://people.debian.org/~aurel32/qemu/mips/' - 'vmlinux-3.2.0-4-5kc-malta') - kernel_hash = 'db6eea7de35d36c77d8c165b6bcb222e16eb91db' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - self.check_mips_malta('be', kernel_path, 'mips64') + self.check_mips_malta('mips64', 'be') def test_mips_malta64el_kernel3_2_0(self): """ @@ -244,8 +258,4 @@ class LinuxSSH(Test): :avocado: tags=endian:little :avocado: tags=device:pcnet32 """ - kernel_url = ('https://people.debian.org/~aurel32/qemu/mipsel/' - 'vmlinux-3.2.0-4-5kc-malta') - kernel_hash = '6a7f77245acf231415a0e8b725d91ed2f3487794' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - self.check_mips_malta('le', kernel_path, 'mips64') + self.check_mips_malta('mips64', 'le') diff --git a/tests/benchmark-crypto-cipher.c b/tests/benchmark-crypto-cipher.c index 67fdf8c31d..53032334ec 100644 --- a/tests/benchmark-crypto-cipher.c +++ b/tests/benchmark-crypto-cipher.c @@ -21,11 +21,12 @@ static void test_cipher_speed(size_t chunk_size, { QCryptoCipher *cipher; Error *err = NULL; - double total = 0.0; uint8_t *key = NULL, *iv = NULL; uint8_t *plaintext = NULL, *ciphertext = NULL; size_t nkey; size_t niv; + const size_t total = 2 * GiB; + size_t remain; if (!qcrypto_cipher_supports(alg, mode)) { return; @@ -58,33 +59,34 @@ static void test_cipher_speed(size_t chunk_size, &err) == 0); g_test_timer_start(); - do { + remain = total; + while (remain) { g_assert(qcrypto_cipher_encrypt(cipher, plaintext, ciphertext, chunk_size, &err) == 0); - total += chunk_size; - } while (g_test_timer_elapsed() < 1.0); + remain -= chunk_size; + } + g_test_timer_elapsed(); - total /= MiB; g_print("Enc chunk %zu bytes ", chunk_size); - g_print("%.2f MB/sec ", total / g_test_timer_last()); + g_print("%.2f MB/sec ", (double)total / MiB / g_test_timer_last()); - total = 0.0; g_test_timer_start(); - do { + remain = total; + while (remain) { g_assert(qcrypto_cipher_decrypt(cipher, plaintext, ciphertext, chunk_size, &err) == 0); - total += chunk_size; - } while (g_test_timer_elapsed() < 1.0); + remain -= chunk_size; + } + g_test_timer_elapsed(); - total /= MiB; g_print("Dec chunk %zu bytes ", chunk_size); - g_print("%.2f MB/sec ", total / g_test_timer_last()); + g_print("%.2f MB/sec ", (double)total / MiB / g_test_timer_last()); qcrypto_cipher_free(cipher); g_free(plaintext); @@ -161,15 +163,26 @@ static void test_cipher_speed_xts_aes_256(const void *opaque) int main(int argc, char **argv) { + char *alg = NULL; + char *size = NULL; g_test_init(&argc, &argv, NULL); g_assert(qcrypto_init(NULL) == 0); #define ADD_TEST(mode, cipher, keysize, chunk) \ - g_test_add_data_func( \ + if ((!alg || g_str_equal(alg, #mode)) && \ + (!size || g_str_equal(size, #chunk))) \ + g_test_add_data_func( \ "/crypto/cipher/" #mode "-" #cipher "-" #keysize "/chunk-" #chunk, \ (void *)chunk, \ test_cipher_speed_ ## mode ## _ ## cipher ## _ ## keysize) + if (argc >= 2) { + alg = argv[1]; + } + if (argc >= 3) { + size = argv[2]; + } + #define ADD_TESTS(chunk) \ do { \ ADD_TEST(ecb, aes, 128, chunk); \ diff --git a/tests/benchmark-crypto-hash.c b/tests/benchmark-crypto-hash.c index 9b6f7a9155..7f659f7323 100644 --- a/tests/benchmark-crypto-hash.c +++ b/tests/benchmark-crypto-hash.c @@ -20,7 +20,8 @@ static void test_hash_speed(const void *opaque) size_t chunk_size = (size_t)opaque; uint8_t *in = NULL, *out = NULL; size_t out_len = 0; - double total = 0.0; + const size_t total = 2 * GiB; + size_t remain; struct iovec iov; int ret; @@ -31,20 +32,20 @@ static void test_hash_speed(const void *opaque) iov.iov_len = chunk_size; g_test_timer_start(); - do { + remain = total; + while (remain) { ret = qcrypto_hash_bytesv(QCRYPTO_HASH_ALG_SHA256, &iov, 1, &out, &out_len, NULL); g_assert(ret == 0); - total += chunk_size; - } while (g_test_timer_elapsed() < 5.0); + remain -= chunk_size; + } + g_test_timer_elapsed(); - total /= MiB; g_print("sha256: "); - g_print("Testing chunk_size %zu bytes ", chunk_size); - g_print("done: %.2f MB in %.2f secs: ", total, g_test_timer_last()); - g_print("%.2f MB/sec\n", total / g_test_timer_last()); + g_print("Hash %zu GB chunk size %zu bytes ", total / GiB, chunk_size); + g_print("%.2f MB/sec ", (double)total / MiB / g_test_timer_last()); g_free(out); g_free(in); diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index 89c56a3a88..19dbe26169 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -2,6 +2,8 @@ .PHONY: docker docker-test docker-clean docker-image docker-qemu-src +HOST_ARCH = $(if $(ARCH),$(ARCH),$(shell uname -m)) + DOCKER_SUFFIX := .docker DOCKER_FILES_DIR := $(SRC_PATH)/tests/docker/dockerfiles # we don't run tests on intermediate images (used as base by another image) @@ -88,7 +90,7 @@ endif # Enforce dependencies for composite images docker-image-debian9-mxe: docker-image-debian9 -ifeq ($(ARCH),x86_64) +ifeq ($(HOST_ARCH),x86_64) docker-image-debian-amd64: docker-image-debian9 DOCKER_PARTIAL_IMAGES += debian-amd64-cross else @@ -106,7 +108,7 @@ docker-image-debian-win32-cross: docker-image-debian9-mxe docker-image-debian-win64-cross: docker-image-debian9-mxe # For non-x86 hosts not all cross-compilers have been packaged -ifneq ($(ARCH),x86_64) +ifneq ($(HOST_ARCH),x86_64) DOCKER_PARTIAL_IMAGES += debian-mips-cross debian-mipsel-cross debian-mips64el-cross DOCKER_PARTIAL_IMAGES += debian-ppc64el-cross DOCKER_PARTIAL_IMAGES += debian-s390x-cross diff --git a/tests/docker/dockerfiles/travis.docker b/tests/docker/dockerfiles/travis.docker index ea14da29d9..e8eb48dccf 100644 --- a/tests/docker/dockerfiles/travis.docker +++ b/tests/docker/dockerfiles/travis.docker @@ -1,4 +1,8 @@ -FROM travisci/ci-sardonyx:packer-1546978056-2c98a19 +# +# Travis Image - this is broadly the same image that we run our CI +# tests on. +# +FROM travisci/ci-sardonyx:packer-1552557266-f909ac5 ENV DEBIAN_FRONTEND noninteractive ENV LANG en_US.UTF-8 ENV LC_ALL en_US.UTF-8 diff --git a/tests/libqos/pci.c b/tests/libqos/pci.c index 662ee7a517..2309a724e4 100644 --- a/tests/libqos/pci.c +++ b/tests/libqos/pci.c @@ -115,10 +115,28 @@ void qpci_device_enable(QPCIDevice *dev) g_assert_cmphex(cmd & PCI_COMMAND_MASTER, ==, PCI_COMMAND_MASTER); } -uint8_t qpci_find_capability(QPCIDevice *dev, uint8_t id) +/** + * qpci_find_capability: + * @dev: the PCI device + * @id: the PCI Capability ID (PCI_CAP_ID_*) + * @start_addr: 0 to begin iteration or the last return value to continue + * iteration + * + * Iterate over the PCI Capabilities List. + * + * Returns: PCI Configuration Space offset of the capabililty structure or + * 0 if no further matching capability is found + */ +uint8_t qpci_find_capability(QPCIDevice *dev, uint8_t id, uint8_t start_addr) { uint8_t cap; - uint8_t addr = qpci_config_readb(dev, PCI_CAPABILITY_LIST); + uint8_t addr; + + if (start_addr) { + addr = qpci_config_readb(dev, start_addr + PCI_CAP_LIST_NEXT); + } else { + addr = qpci_config_readb(dev, PCI_CAPABILITY_LIST); + } do { cap = qpci_config_readb(dev, addr); @@ -138,7 +156,7 @@ void qpci_msix_enable(QPCIDevice *dev) uint8_t bir_table; uint8_t bir_pba; - addr = qpci_find_capability(dev, PCI_CAP_ID_MSIX); + addr = qpci_find_capability(dev, PCI_CAP_ID_MSIX, 0); g_assert_cmphex(addr, !=, 0); val = qpci_config_readw(dev, addr + PCI_MSIX_FLAGS); @@ -167,7 +185,7 @@ void qpci_msix_disable(QPCIDevice *dev) uint16_t val; g_assert(dev->msix_enabled); - addr = qpci_find_capability(dev, PCI_CAP_ID_MSIX); + addr = qpci_find_capability(dev, PCI_CAP_ID_MSIX, 0); g_assert_cmphex(addr, !=, 0); val = qpci_config_readw(dev, addr + PCI_MSIX_FLAGS); qpci_config_writew(dev, addr + PCI_MSIX_FLAGS, @@ -203,7 +221,7 @@ bool qpci_msix_masked(QPCIDevice *dev, uint16_t entry) uint64_t vector_off = dev->msix_table_off + entry * PCI_MSIX_ENTRY_SIZE; g_assert(dev->msix_enabled); - addr = qpci_find_capability(dev, PCI_CAP_ID_MSIX); + addr = qpci_find_capability(dev, PCI_CAP_ID_MSIX, 0); g_assert_cmphex(addr, !=, 0); val = qpci_config_readw(dev, addr + PCI_MSIX_FLAGS); @@ -221,7 +239,7 @@ uint16_t qpci_msix_table_size(QPCIDevice *dev) uint8_t addr; uint16_t control; - addr = qpci_find_capability(dev, PCI_CAP_ID_MSIX); + addr = qpci_find_capability(dev, PCI_CAP_ID_MSIX, 0); g_assert_cmphex(addr, !=, 0); control = qpci_config_readw(dev, addr + PCI_MSIX_FLAGS); diff --git a/tests/libqos/pci.h b/tests/libqos/pci.h index a5389a5845..590c175190 100644 --- a/tests/libqos/pci.h +++ b/tests/libqos/pci.h @@ -86,7 +86,7 @@ bool qpci_has_buggy_msi(QPCIDevice *dev); bool qpci_check_buggy_msi(QPCIDevice *dev); void qpci_device_enable(QPCIDevice *dev); -uint8_t qpci_find_capability(QPCIDevice *dev, uint8_t id); +uint8_t qpci_find_capability(QPCIDevice *dev, uint8_t id, uint8_t start_addr); void qpci_msix_enable(QPCIDevice *dev); void qpci_msix_disable(QPCIDevice *dev); bool qpci_msix_pending(QPCIDevice *dev, uint16_t entry); diff --git a/tests/libqos/virtio-9p.c b/tests/libqos/virtio-9p.c index 8c9efce3e1..77dbfb62ad 100644 --- a/tests/libqos/virtio-9p.c +++ b/tests/libqos/virtio-9p.c @@ -32,6 +32,12 @@ static void virtio_9p_cleanup(QVirtio9P *interface) static void virtio_9p_setup(QVirtio9P *interface) { + uint64_t features; + + features = qvirtio_get_features(interface->vdev); + features &= ~(QVIRTIO_F_BAD_FEATURE | (1ull << VIRTIO_RING_F_EVENT_IDX)); + qvirtio_set_features(interface->vdev, features); + interface->vq = qvirtqueue_setup(interface->vdev, alloc, 0); qvirtio_set_driver_ok(interface->vdev); } diff --git a/tests/libqos/virtio-mmio.c b/tests/libqos/virtio-mmio.c index d0047876a8..e0a2bd7bc6 100644 --- a/tests/libqos/virtio-mmio.c +++ b/tests/libqos/virtio-mmio.c @@ -40,22 +40,38 @@ static uint64_t qvirtio_mmio_config_readq(QVirtioDevice *d, uint64_t off) return qtest_readq(dev->qts, dev->addr + QVIRTIO_MMIO_DEVICE_SPECIFIC + off); } -static uint32_t qvirtio_mmio_get_features(QVirtioDevice *d) +static uint64_t qvirtio_mmio_get_features(QVirtioDevice *d) { QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev); + uint64_t lo; + uint64_t hi = 0; + qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_HOST_FEATURES_SEL, 0); - return qtest_readl(dev->qts, dev->addr + QVIRTIO_MMIO_HOST_FEATURES); + lo = qtest_readl(dev->qts, dev->addr + QVIRTIO_MMIO_HOST_FEATURES); + + if (dev->version >= 2) { + qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_HOST_FEATURES_SEL, 1); + hi = qtest_readl(dev->qts, dev->addr + QVIRTIO_MMIO_HOST_FEATURES); + } + + return (hi << 32) | lo; } -static void qvirtio_mmio_set_features(QVirtioDevice *d, uint32_t features) +static void qvirtio_mmio_set_features(QVirtioDevice *d, uint64_t features) { QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev); dev->features = features; qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_GUEST_FEATURES_SEL, 0); qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_GUEST_FEATURES, features); + + if (dev->version >= 2) { + qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_GUEST_FEATURES_SEL, 1); + qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_GUEST_FEATURES, + features >> 32); + } } -static uint32_t qvirtio_mmio_get_guest_features(QVirtioDevice *d) +static uint64_t qvirtio_mmio_get_guest_features(QVirtioDevice *d) { QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev); return dev->features; @@ -127,9 +143,11 @@ static uint16_t qvirtio_mmio_get_queue_size(QVirtioDevice *d) return (uint16_t)qtest_readl(dev->qts, dev->addr + QVIRTIO_MMIO_QUEUE_NUM_MAX); } -static void qvirtio_mmio_set_queue_address(QVirtioDevice *d, uint32_t pfn) +static void qvirtio_mmio_set_queue_address(QVirtioDevice *d, QVirtQueue *vq) { QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev); + uint64_t pfn = vq->desc / dev->page_size; + qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_QUEUE_PFN, pfn); } @@ -141,6 +159,7 @@ static QVirtQueue *qvirtio_mmio_virtqueue_setup(QVirtioDevice *d, uint64_t addr; vq = g_malloc0(sizeof(*vq)); + vq->vdev = d; qvirtio_mmio_queue_select(d, index); qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_QUEUE_ALIGN, dev->page_size); @@ -149,8 +168,8 @@ static QVirtQueue *qvirtio_mmio_virtqueue_setup(QVirtioDevice *d, vq->free_head = 0; vq->num_free = vq->size; vq->align = dev->page_size; - vq->indirect = (dev->features & (1u << VIRTIO_RING_F_INDIRECT_DESC)) != 0; - vq->event = (dev->features & (1u << VIRTIO_RING_F_EVENT_IDX)) != 0; + vq->indirect = dev->features & (1ull << VIRTIO_RING_F_INDIRECT_DESC); + vq->event = dev->features & (1ull << VIRTIO_RING_F_EVENT_IDX); qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_QUEUE_NUM, vq->size); @@ -162,7 +181,7 @@ static QVirtQueue *qvirtio_mmio_virtqueue_setup(QVirtioDevice *d, addr = guest_alloc(alloc, qvring_size(vq->size, dev->page_size)); qvring_init(dev->qts, alloc, vq, addr); - qvirtio_mmio_set_queue_address(d, vq->desc / dev->page_size); + qvirtio_mmio_set_queue_address(d, vq); return vq; } @@ -223,6 +242,9 @@ void qvirtio_mmio_init_device(QVirtioMMIODevice *dev, QTestState *qts, magic = qtest_readl(qts, addr + QVIRTIO_MMIO_MAGIC_VALUE); g_assert(magic == ('v' | 'i' << 8 | 'r' << 16 | 't' << 24)); + dev->version = qtest_readl(qts, addr + QVIRTIO_MMIO_VERSION); + g_assert(dev->version == 1 || dev->version == 2); + dev->qts = qts; dev->addr = addr; dev->page_size = page_size; diff --git a/tests/libqos/virtio-mmio.h b/tests/libqos/virtio-mmio.h index 17a17141c3..0e45778b07 100644 --- a/tests/libqos/virtio-mmio.h +++ b/tests/libqos/virtio-mmio.h @@ -40,6 +40,7 @@ typedef struct QVirtioMMIODevice { uint64_t addr; uint32_t page_size; uint32_t features; /* As it cannot be read later, save it */ + uint32_t version; } QVirtioMMIODevice; extern const QVirtioBus qvirtio_mmio; diff --git a/tests/libqos/virtio-net.c b/tests/libqos/virtio-net.c index 6567beb553..710d440c3d 100644 --- a/tests/libqos/virtio-net.c +++ b/tests/libqos/virtio-net.c @@ -44,11 +44,11 @@ static void virtio_net_setup(QVirtioNet *interface) features = qvirtio_get_features(vdev); features &= ~(QVIRTIO_F_BAD_FEATURE | - (1u << VIRTIO_RING_F_INDIRECT_DESC) | - (1u << VIRTIO_RING_F_EVENT_IDX)); + (1ull << VIRTIO_RING_F_INDIRECT_DESC) | + (1ull << VIRTIO_RING_F_EVENT_IDX)); qvirtio_set_features(vdev, features); - if (features & (1u << VIRTIO_NET_F_MQ)) { + if (features & (1ull << VIRTIO_NET_F_MQ)) { interface->n_queues = qvirtio_config_readw(vdev, 8) * 2; } else { interface->n_queues = 2; diff --git a/tests/libqos/virtio-pci-modern.c b/tests/libqos/virtio-pci-modern.c new file mode 100644 index 0000000000..18d118866f --- /dev/null +++ b/tests/libqos/virtio-pci-modern.c @@ -0,0 +1,443 @@ +/* + * libqos VIRTIO 1.0 PCI driver + * + * Copyright (c) 2019 Red Hat, Inc + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "standard-headers/linux/pci_regs.h" +#include "standard-headers/linux/virtio_pci.h" +#include "standard-headers/linux/virtio_config.h" +#include "virtio-pci-modern.h" + +static uint8_t config_readb(QVirtioDevice *d, uint64_t addr) +{ + QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); + return qpci_io_readb(dev->pdev, dev->bar, dev->device_cfg_offset + addr); +} + +static uint16_t config_readw(QVirtioDevice *d, uint64_t addr) +{ + QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); + return qpci_io_readw(dev->pdev, dev->bar, dev->device_cfg_offset + addr); +} + +static uint32_t config_readl(QVirtioDevice *d, uint64_t addr) +{ + QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); + return qpci_io_readl(dev->pdev, dev->bar, dev->device_cfg_offset + addr); +} + +static uint64_t config_readq(QVirtioDevice *d, uint64_t addr) +{ + QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); + return qpci_io_readq(dev->pdev, dev->bar, dev->device_cfg_offset + addr); +} + +static uint64_t get_features(QVirtioDevice *d) +{ + QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); + uint64_t lo, hi; + + qpci_io_writel(dev->pdev, dev->bar, dev->common_cfg_offset + + offsetof(struct virtio_pci_common_cfg, + device_feature_select), + 0); + lo = qpci_io_readl(dev->pdev, dev->bar, dev->common_cfg_offset + + offsetof(struct virtio_pci_common_cfg, device_feature)); + + qpci_io_writel(dev->pdev, dev->bar, dev->common_cfg_offset + + offsetof(struct virtio_pci_common_cfg, + device_feature_select), + 1); + hi = qpci_io_readl(dev->pdev, dev->bar, dev->common_cfg_offset + + offsetof(struct virtio_pci_common_cfg, device_feature)); + + return (hi << 32) | lo; +} + +static void set_features(QVirtioDevice *d, uint64_t features) +{ + QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); + + /* Drivers must enable VIRTIO 1.0 or else use the Legacy interface */ + g_assert_cmphex(features & (1ull << VIRTIO_F_VERSION_1), !=, 0); + + qpci_io_writel(dev->pdev, dev->bar, dev->common_cfg_offset + + offsetof(struct virtio_pci_common_cfg, + guest_feature_select), + 0); + qpci_io_writel(dev->pdev, dev->bar, dev->common_cfg_offset + + offsetof(struct virtio_pci_common_cfg, + guest_feature), + features); + qpci_io_writel(dev->pdev, dev->bar, dev->common_cfg_offset + + offsetof(struct virtio_pci_common_cfg, + guest_feature_select), + 1); + qpci_io_writel(dev->pdev, dev->bar, dev->common_cfg_offset + + offsetof(struct virtio_pci_common_cfg, + guest_feature), + features >> 32); +} + +static uint64_t get_guest_features(QVirtioDevice *d) +{ + QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); + uint64_t lo, hi; + + qpci_io_writel(dev->pdev, dev->bar, dev->common_cfg_offset + + offsetof(struct virtio_pci_common_cfg, + guest_feature_select), + 0); + lo = qpci_io_readl(dev->pdev, dev->bar, dev->common_cfg_offset + + offsetof(struct virtio_pci_common_cfg, guest_feature)); + + qpci_io_writel(dev->pdev, dev->bar, dev->common_cfg_offset + + offsetof(struct virtio_pci_common_cfg, + guest_feature_select), + 1); + hi = qpci_io_readl(dev->pdev, dev->bar, dev->common_cfg_offset + + offsetof(struct virtio_pci_common_cfg, guest_feature)); + + return (hi << 32) | lo; +} + +static uint8_t get_status(QVirtioDevice *d) +{ + QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); + + return qpci_io_readb(dev->pdev, dev->bar, dev->common_cfg_offset + + offsetof(struct virtio_pci_common_cfg, + device_status)); +} + +static void set_status(QVirtioDevice *d, uint8_t status) +{ + QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); + + return qpci_io_writeb(dev->pdev, dev->bar, dev->common_cfg_offset + + offsetof(struct virtio_pci_common_cfg, + device_status), + status); +} + +static bool get_msix_status(QVirtioPCIDevice *dev, uint32_t msix_entry, + uint32_t msix_addr, uint32_t msix_data) +{ + uint32_t data; + + g_assert_cmpint(msix_entry, !=, -1); + if (qpci_msix_masked(dev->pdev, msix_entry)) { + /* No ISR checking should be done if masked, but read anyway */ + return qpci_msix_pending(dev->pdev, msix_entry); + } + + data = qtest_readl(dev->pdev->bus->qts, msix_addr); + if (data == msix_data) { + qtest_writel(dev->pdev->bus->qts, msix_addr, 0); + return true; + } else { + return false; + } +} + +static bool get_queue_isr_status(QVirtioDevice *d, QVirtQueue *vq) +{ + QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); + + if (dev->pdev->msix_enabled) { + QVirtQueuePCI *vqpci = container_of(vq, QVirtQueuePCI, vq); + + return get_msix_status(dev, vqpci->msix_entry, vqpci->msix_addr, + vqpci->msix_data); + } + + return qpci_io_readb(dev->pdev, dev->bar, dev->isr_cfg_offset) & 1; +} + +static bool get_config_isr_status(QVirtioDevice *d) +{ + QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); + + if (dev->pdev->msix_enabled) { + return get_msix_status(dev, dev->config_msix_entry, + dev->config_msix_addr, dev->config_msix_data); + } + + return qpci_io_readb(dev->pdev, dev->bar, dev->isr_cfg_offset) & 2; +} + +static void wait_config_isr_status(QVirtioDevice *d, gint64 timeout_us) +{ + QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); + gint64 start_time = g_get_monotonic_time(); + + do { + g_assert(g_get_monotonic_time() - start_time <= timeout_us); + qtest_clock_step(dev->pdev->bus->qts, 100); + } while (!get_config_isr_status(d)); +} + +static void queue_select(QVirtioDevice *d, uint16_t index) +{ + QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); + + qpci_io_writew(dev->pdev, dev->bar, dev->common_cfg_offset + + offsetof(struct virtio_pci_common_cfg, queue_select), + index); +} + +static uint16_t get_queue_size(QVirtioDevice *d) +{ + QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); + + return qpci_io_readw(dev->pdev, dev->bar, dev->common_cfg_offset + + offsetof(struct virtio_pci_common_cfg, queue_size)); +} + +static void set_queue_address(QVirtioDevice *d, QVirtQueue *vq) +{ + QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); + + qpci_io_writel(dev->pdev, dev->bar, dev->common_cfg_offset + + offsetof(struct virtio_pci_common_cfg, queue_desc_lo), + vq->desc); + qpci_io_writel(dev->pdev, dev->bar, dev->common_cfg_offset + + offsetof(struct virtio_pci_common_cfg, queue_desc_hi), + vq->desc >> 32); + + qpci_io_writel(dev->pdev, dev->bar, dev->common_cfg_offset + + offsetof(struct virtio_pci_common_cfg, queue_avail_lo), + vq->avail); + qpci_io_writel(dev->pdev, dev->bar, dev->common_cfg_offset + + offsetof(struct virtio_pci_common_cfg, queue_avail_hi), + vq->avail >> 32); + + qpci_io_writel(dev->pdev, dev->bar, dev->common_cfg_offset + + offsetof(struct virtio_pci_common_cfg, queue_used_lo), + vq->used); + qpci_io_writel(dev->pdev, dev->bar, dev->common_cfg_offset + + offsetof(struct virtio_pci_common_cfg, queue_used_hi), + vq->used >> 32); +} + +static QVirtQueue *virtqueue_setup(QVirtioDevice *d, QGuestAllocator *alloc, + uint16_t index) +{ + QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); + QVirtQueue *vq; + QVirtQueuePCI *vqpci; + uint16_t notify_off; + + vq = qvirtio_pci_virtqueue_setup_common(d, alloc, index); + vqpci = container_of(vq, QVirtQueuePCI, vq); + + notify_off = qpci_io_readw(dev->pdev, dev->bar, dev->common_cfg_offset + + offsetof(struct virtio_pci_common_cfg, + queue_notify_off)); + + vqpci->notify_offset = dev->notify_cfg_offset + + notify_off * dev->notify_off_multiplier; + + qpci_io_writew(dev->pdev, dev->bar, dev->common_cfg_offset + + offsetof(struct virtio_pci_common_cfg, queue_enable), 1); + + return vq; +} + +static void virtqueue_kick(QVirtioDevice *d, QVirtQueue *vq) +{ + QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); + QVirtQueuePCI *vqpci = container_of(vq, QVirtQueuePCI, vq); + + qpci_io_writew(dev->pdev, dev->bar, vqpci->notify_offset, vq->index); +} + +static const QVirtioBus qvirtio_pci_virtio_1 = { + .config_readb = config_readb, + .config_readw = config_readw, + .config_readl = config_readl, + .config_readq = config_readq, + .get_features = get_features, + .set_features = set_features, + .get_guest_features = get_guest_features, + .get_status = get_status, + .set_status = set_status, + .get_queue_isr_status = get_queue_isr_status, + .wait_config_isr_status = wait_config_isr_status, + .queue_select = queue_select, + .get_queue_size = get_queue_size, + .set_queue_address = set_queue_address, + .virtqueue_setup = virtqueue_setup, + .virtqueue_cleanup = qvirtio_pci_virtqueue_cleanup_common, + .virtqueue_kick = virtqueue_kick, +}; + +static void set_config_vector(QVirtioPCIDevice *d, uint16_t entry) +{ + uint16_t vector; + + qpci_io_writew(d->pdev, d->bar, d->common_cfg_offset + + offsetof(struct virtio_pci_common_cfg, msix_config), entry); + vector = qpci_io_readw(d->pdev, d->bar, d->common_cfg_offset + + offsetof(struct virtio_pci_common_cfg, + msix_config)); + g_assert_cmphex(vector, !=, VIRTIO_MSI_NO_VECTOR); +} + +static void set_queue_vector(QVirtioPCIDevice *d, uint16_t vq_idx, + uint16_t entry) +{ + uint16_t vector; + + queue_select(&d->vdev, vq_idx); + qpci_io_writew(d->pdev, d->bar, d->common_cfg_offset + + offsetof(struct virtio_pci_common_cfg, queue_msix_vector), + entry); + vector = qpci_io_readw(d->pdev, d->bar, d->common_cfg_offset + + offsetof(struct virtio_pci_common_cfg, + queue_msix_vector)); + g_assert_cmphex(vector, !=, VIRTIO_MSI_NO_VECTOR); +} + +static const QVirtioPCIMSIXOps qvirtio_pci_msix_ops_virtio_1 = { + .set_config_vector = set_config_vector, + .set_queue_vector = set_queue_vector, +}; + +static bool probe_device_type(QVirtioPCIDevice *dev) +{ + uint16_t vendor_id; + uint16_t device_id; + + /* "Drivers MUST match devices with the PCI Vendor ID 0x1AF4" */ + vendor_id = qpci_config_readw(dev->pdev, PCI_VENDOR_ID); + if (vendor_id != 0x1af4) { + return false; + } + + /* + * "Any PCI device with ... PCI Device ID 0x1000 through 0x107F inclusive + * is a virtio device" + */ + device_id = qpci_config_readw(dev->pdev, PCI_DEVICE_ID); + if (device_id < 0x1000 || device_id > 0x107f) { + return false; + } + + /* + * "Devices MAY utilize a Transitional PCI Device ID range, 0x1000 to + * 0x103F depending on the device type" + */ + if (device_id < 0x1040) { + /* + * "Transitional devices MUST have the PCI Subsystem Device ID matching + * the Virtio Device ID" + */ + dev->vdev.device_type = qpci_config_readw(dev->pdev, PCI_SUBSYSTEM_ID); + } else { + /* + * "The PCI Device ID is calculated by adding 0x1040 to the Virtio + * Device ID" + */ + dev->vdev.device_type = device_id - 0x1040; + } + + return true; +} + +/* Find the first VIRTIO 1.0 PCI structure for a given type */ +static bool find_structure(QVirtioPCIDevice *dev, uint8_t cfg_type, + uint8_t *bar, uint32_t *offset, uint32_t *length, + uint8_t *cfg_addr) +{ + uint8_t addr = 0; + + while ((addr = qpci_find_capability(dev->pdev, PCI_CAP_ID_VNDR, + addr)) != 0) { + uint8_t type; + + type = qpci_config_readb(dev->pdev, + addr + offsetof(struct virtio_pci_cap, cfg_type)); + if (type != cfg_type) { + continue; + } + + *bar = qpci_config_readb(dev->pdev, + addr + offsetof(struct virtio_pci_cap, bar)); + *offset = qpci_config_readl(dev->pdev, + addr + offsetof(struct virtio_pci_cap, offset)); + *length = qpci_config_readl(dev->pdev, + addr + offsetof(struct virtio_pci_cap, length)); + if (cfg_addr) { + *cfg_addr = addr; + } + + return true; + } + + return false; +} + +static bool probe_device_layout(QVirtioPCIDevice *dev) +{ + uint8_t bar; + uint8_t cfg_addr; + uint32_t length; + + /* + * Due to the qpci_iomap() API we only support devices that put all + * structures in the same PCI BAR. Luckily this is true with QEMU. + */ + + if (!find_structure(dev, VIRTIO_PCI_CAP_COMMON_CFG, &bar, + &dev->common_cfg_offset, &length, NULL)) { + return false; + } + dev->bar_idx = bar; + + if (!find_structure(dev, VIRTIO_PCI_CAP_NOTIFY_CFG, &bar, + &dev->notify_cfg_offset, &length, &cfg_addr)) { + return false; + } + g_assert_cmphex(bar, ==, dev->bar_idx); + + dev->notify_off_multiplier = qpci_config_readl(dev->pdev, + cfg_addr + offsetof(struct virtio_pci_notify_cap, + notify_off_multiplier)); + + if (!find_structure(dev, VIRTIO_PCI_CAP_ISR_CFG, &bar, + &dev->isr_cfg_offset, &length, NULL)) { + return false; + } + g_assert_cmphex(bar, ==, dev->bar_idx); + + if (!find_structure(dev, VIRTIO_PCI_CAP_DEVICE_CFG, &bar, + &dev->device_cfg_offset, &length, NULL)) { + return false; + } + g_assert_cmphex(bar, ==, dev->bar_idx); + + return true; +} + +/* Probe a VIRTIO 1.0 device */ +bool qvirtio_pci_init_virtio_1(QVirtioPCIDevice *dev) +{ + if (!probe_device_type(dev)) { + return false; + } + + if (!probe_device_layout(dev)) { + return false; + } + + dev->vdev.bus = &qvirtio_pci_virtio_1; + dev->msix_ops = &qvirtio_pci_msix_ops_virtio_1; + dev->vdev.big_endian = false; + return true; +} diff --git a/tests/libqos/virtio-pci-modern.h b/tests/libqos/virtio-pci-modern.h new file mode 100644 index 0000000000..6bf2b207c3 --- /dev/null +++ b/tests/libqos/virtio-pci-modern.h @@ -0,0 +1,17 @@ +/* + * libqos virtio PCI VIRTIO 1.0 definitions + * + * Copyright (c) 2019 Red Hat, Inc + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef LIBQOS_VIRTIO_PCI_MODERN_H +#define LIBQOS_VIRTIO_PCI_MODERN_H + +#include "virtio-pci.h" + +bool qvirtio_pci_init_virtio_1(QVirtioPCIDevice *dev); + +#endif /* LIBQOS_VIRTIO_PCI_MODERN_H */ diff --git a/tests/libqos/virtio-pci.c b/tests/libqos/virtio-pci.c index 50499e75ef..62851c29bb 100644 --- a/tests/libqos/virtio-pci.c +++ b/tests/libqos/virtio-pci.c @@ -22,6 +22,8 @@ #include "hw/pci/pci.h" #include "hw/pci/pci_regs.h" +#include "virtio-pci-modern.h" + /* virtio-pci is a superclass of all virtio-xxx-pci devices; * the relation between virtio-pci and virtio-xxx-pci is implicit, * and therefore virtio-pci does not produce virtio and is not @@ -35,14 +37,6 @@ * original qvirtio_pci_destructor and qvirtio_pci_start_hw. */ -static inline bool qvirtio_pci_is_big_endian(QVirtioPCIDevice *dev) -{ - QPCIBus *bus = dev->pdev->bus; - - /* FIXME: virtio 1.0 is always little-endian */ - return qtest_big_endian(bus->qts); -} - #define CONFIG_BASE(dev) (VIRTIO_PCI_CONFIG_OFF((dev)->pdev->msix_enabled)) static uint8_t qvirtio_pci_config_readb(QVirtioDevice *d, uint64_t off) @@ -55,8 +49,7 @@ static uint8_t qvirtio_pci_config_readb(QVirtioDevice *d, uint64_t off) * but virtio ( < 1.0) is in guest order * so with a big-endian guest the order has been reversed, * reverse it again - * virtio-1.0 is always little-endian, like PCI, but this - * case will be managed inside qvirtio_pci_is_big_endian() + * virtio-1.0 is always little-endian, like PCI */ static uint16_t qvirtio_pci_config_readw(QVirtioDevice *d, uint64_t off) @@ -96,19 +89,19 @@ static uint64_t qvirtio_pci_config_readq(QVirtioDevice *d, uint64_t off) return val; } -static uint32_t qvirtio_pci_get_features(QVirtioDevice *d) +static uint64_t qvirtio_pci_get_features(QVirtioDevice *d) { QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); return qpci_io_readl(dev->pdev, dev->bar, VIRTIO_PCI_HOST_FEATURES); } -static void qvirtio_pci_set_features(QVirtioDevice *d, uint32_t features) +static void qvirtio_pci_set_features(QVirtioDevice *d, uint64_t features) { QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); qpci_io_writel(dev->pdev, dev->bar, VIRTIO_PCI_GUEST_FEATURES, features); } -static uint32_t qvirtio_pci_get_guest_features(QVirtioDevice *d) +static uint64_t qvirtio_pci_get_guest_features(QVirtioDevice *d) { QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); return qpci_io_readl(dev->pdev, dev->bar, VIRTIO_PCI_GUEST_FEATURES); @@ -199,31 +192,35 @@ static uint16_t qvirtio_pci_get_queue_size(QVirtioDevice *d) return qpci_io_readw(dev->pdev, dev->bar, VIRTIO_PCI_QUEUE_NUM); } -static void qvirtio_pci_set_queue_address(QVirtioDevice *d, uint32_t pfn) +static void qvirtio_pci_set_queue_address(QVirtioDevice *d, QVirtQueue *vq) { QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); + uint64_t pfn = vq->desc / VIRTIO_PCI_VRING_ALIGN; + qpci_io_writel(dev->pdev, dev->bar, VIRTIO_PCI_QUEUE_PFN, pfn); } -static QVirtQueue *qvirtio_pci_virtqueue_setup(QVirtioDevice *d, - QGuestAllocator *alloc, uint16_t index) +QVirtQueue *qvirtio_pci_virtqueue_setup_common(QVirtioDevice *d, + QGuestAllocator *alloc, + uint16_t index) { - uint32_t feat; + uint64_t feat; uint64_t addr; QVirtQueuePCI *vqpci; QVirtioPCIDevice *qvpcidev = container_of(d, QVirtioPCIDevice, vdev); vqpci = g_malloc0(sizeof(*vqpci)); - feat = qvirtio_pci_get_guest_features(d); + feat = d->bus->get_guest_features(d); - qvirtio_pci_queue_select(d, index); + d->bus->queue_select(d, index); + vqpci->vq.vdev = d; vqpci->vq.index = index; - vqpci->vq.size = qvirtio_pci_get_queue_size(d); + vqpci->vq.size = d->bus->get_queue_size(d); vqpci->vq.free_head = 0; vqpci->vq.num_free = vqpci->vq.size; vqpci->vq.align = VIRTIO_PCI_VRING_ALIGN; - vqpci->vq.indirect = (feat & (1u << VIRTIO_RING_F_INDIRECT_DESC)) != 0; - vqpci->vq.event = (feat & (1u << VIRTIO_RING_F_EVENT_IDX)) != 0; + vqpci->vq.indirect = feat & (1ull << VIRTIO_RING_F_INDIRECT_DESC); + vqpci->vq.event = feat & (1ull << VIRTIO_RING_F_EVENT_IDX); vqpci->msix_entry = -1; vqpci->msix_addr = 0; @@ -238,12 +235,12 @@ static QVirtQueue *qvirtio_pci_virtqueue_setup(QVirtioDevice *d, addr = guest_alloc(alloc, qvring_size(vqpci->vq.size, VIRTIO_PCI_VRING_ALIGN)); qvring_init(qvpcidev->pdev->bus->qts, alloc, &vqpci->vq, addr); - qvirtio_pci_set_queue_address(d, vqpci->vq.desc / VIRTIO_PCI_VRING_ALIGN); + d->bus->set_queue_address(d, &vqpci->vq); return &vqpci->vq; } -static void qvirtio_pci_virtqueue_cleanup(QVirtQueue *vq, +void qvirtio_pci_virtqueue_cleanup_common(QVirtQueue *vq, QGuestAllocator *alloc) { QVirtQueuePCI *vqpci = container_of(vq, QVirtQueuePCI, vq); @@ -258,7 +255,7 @@ static void qvirtio_pci_virtqueue_kick(QVirtioDevice *d, QVirtQueue *vq) qpci_io_writew(dev->pdev, dev->bar, VIRTIO_PCI_QUEUE_NOTIFY, vq->index); } -const QVirtioBus qvirtio_pci = { +static const QVirtioBus qvirtio_pci_legacy = { .config_readb = qvirtio_pci_config_readb, .config_readw = qvirtio_pci_config_readw, .config_readl = qvirtio_pci_config_readl, @@ -273,15 +270,40 @@ const QVirtioBus qvirtio_pci = { .queue_select = qvirtio_pci_queue_select, .get_queue_size = qvirtio_pci_get_queue_size, .set_queue_address = qvirtio_pci_set_queue_address, - .virtqueue_setup = qvirtio_pci_virtqueue_setup, - .virtqueue_cleanup = qvirtio_pci_virtqueue_cleanup, + .virtqueue_setup = qvirtio_pci_virtqueue_setup_common, + .virtqueue_cleanup = qvirtio_pci_virtqueue_cleanup_common, .virtqueue_kick = qvirtio_pci_virtqueue_kick, }; +static void qvirtio_pci_set_config_vector(QVirtioPCIDevice *d, uint16_t entry) +{ + uint16_t vector; + + qpci_io_writew(d->pdev, d->bar, VIRTIO_MSI_CONFIG_VECTOR, entry); + vector = qpci_io_readw(d->pdev, d->bar, VIRTIO_MSI_CONFIG_VECTOR); + g_assert_cmphex(vector, !=, VIRTIO_MSI_NO_VECTOR); +} + +static void qvirtio_pci_set_queue_vector(QVirtioPCIDevice *d, uint16_t vq_idx, + uint16_t entry) +{ + uint16_t vector; + + qvirtio_pci_queue_select(&d->vdev, vq_idx); + qpci_io_writew(d->pdev, d->bar, VIRTIO_MSI_QUEUE_VECTOR, entry); + vector = qpci_io_readw(d->pdev, d->bar, VIRTIO_MSI_QUEUE_VECTOR); + g_assert_cmphex(vector, !=, VIRTIO_MSI_NO_VECTOR); +} + +static const QVirtioPCIMSIXOps qvirtio_pci_msix_ops_legacy = { + .set_config_vector = qvirtio_pci_set_config_vector, + .set_queue_vector = qvirtio_pci_set_queue_vector, +}; + void qvirtio_pci_device_enable(QVirtioPCIDevice *d) { qpci_device_enable(d->pdev); - d->bar = qpci_iomap(d->pdev, 0, NULL); + d->bar = qpci_iomap(d->pdev, d->bar_idx, NULL); } void qvirtio_pci_device_disable(QVirtioPCIDevice *d) @@ -292,7 +314,6 @@ void qvirtio_pci_device_disable(QVirtioPCIDevice *d) void qvirtqueue_pci_msix_setup(QVirtioPCIDevice *d, QVirtQueuePCI *vqpci, QGuestAllocator *alloc, uint16_t entry) { - uint16_t vector; uint32_t control; uint64_t off; @@ -318,16 +339,12 @@ void qvirtqueue_pci_msix_setup(QVirtioPCIDevice *d, QVirtQueuePCI *vqpci, off + PCI_MSIX_ENTRY_VECTOR_CTRL, control & ~PCI_MSIX_ENTRY_CTRL_MASKBIT); - qvirtio_pci_queue_select(&d->vdev, vqpci->vq.index); - qpci_io_writew(d->pdev, d->bar, VIRTIO_MSI_QUEUE_VECTOR, entry); - vector = qpci_io_readw(d->pdev, d->bar, VIRTIO_MSI_QUEUE_VECTOR); - g_assert_cmphex(vector, !=, VIRTIO_MSI_NO_VECTOR); + d->msix_ops->set_queue_vector(d, vqpci->vq.index, entry); } void qvirtio_pci_set_msix_configuration_vector(QVirtioPCIDevice *d, QGuestAllocator *alloc, uint16_t entry) { - uint16_t vector; uint32_t control; uint64_t off; @@ -355,9 +372,7 @@ void qvirtio_pci_set_msix_configuration_vector(QVirtioPCIDevice *d, off + PCI_MSIX_ENTRY_VECTOR_CTRL, control & ~PCI_MSIX_ENTRY_CTRL_MASKBIT); - qpci_io_writew(d->pdev, d->bar, VIRTIO_MSI_CONFIG_VECTOR, entry); - vector = qpci_io_readw(d->pdev, d->bar, VIRTIO_MSI_CONFIG_VECTOR); - g_assert_cmphex(vector, !=, VIRTIO_MSI_NO_VECTOR); + d->msix_ops->set_config_vector(d, entry); } void qvirtio_pci_destructor(QOSGraphObject *obj) @@ -374,15 +389,23 @@ void qvirtio_pci_start_hw(QOSGraphObject *obj) qvirtio_start_device(&dev->vdev); } +static void qvirtio_pci_init_legacy(QVirtioPCIDevice *dev) +{ + dev->vdev.device_type = qpci_config_readw(dev->pdev, PCI_SUBSYSTEM_ID); + dev->bar_idx = 0; + dev->vdev.bus = &qvirtio_pci_legacy; + dev->msix_ops = &qvirtio_pci_msix_ops_legacy; + dev->vdev.big_endian = qtest_big_endian(dev->pdev->bus->qts); +} + static void qvirtio_pci_init_from_pcidev(QVirtioPCIDevice *dev, QPCIDevice *pci_dev) { dev->pdev = pci_dev; - dev->vdev.device_type = qpci_config_readw(pci_dev, PCI_SUBSYSTEM_ID); - dev->config_msix_entry = -1; - dev->vdev.bus = &qvirtio_pci; - dev->vdev.big_endian = qvirtio_pci_is_big_endian(dev); + if (!qvirtio_pci_init_virtio_1(dev)) { + qvirtio_pci_init_legacy(dev); + } /* each virtio-xxx-pci device should override at least this function */ dev->obj.get_driver = NULL; diff --git a/tests/libqos/virtio-pci.h b/tests/libqos/virtio-pci.h index 728b4715f1..294d5567ee 100644 --- a/tests/libqos/virtio-pci.h +++ b/tests/libqos/virtio-pci.h @@ -14,24 +14,46 @@ #include "libqos/pci.h" #include "libqos/qgraph.h" +typedef struct QVirtioPCIMSIXOps QVirtioPCIMSIXOps; + typedef struct QVirtioPCIDevice { QOSGraphObject obj; QVirtioDevice vdev; QPCIDevice *pdev; QPCIBar bar; + const QVirtioPCIMSIXOps *msix_ops; uint16_t config_msix_entry; uint64_t config_msix_addr; uint32_t config_msix_data; + + int bar_idx; + + /* VIRTIO 1.0 */ + uint32_t common_cfg_offset; + uint32_t notify_cfg_offset; + uint32_t notify_off_multiplier; + uint32_t isr_cfg_offset; + uint32_t device_cfg_offset; } QVirtioPCIDevice; +struct QVirtioPCIMSIXOps { + /* Set the Configuration Vector for MSI-X */ + void (*set_config_vector)(QVirtioPCIDevice *d, uint16_t entry); + + /* Set the Queue Vector for MSI-X */ + void (*set_queue_vector)(QVirtioPCIDevice *d, uint16_t vq_idx, + uint16_t entry); +}; + typedef struct QVirtQueuePCI { QVirtQueue vq; uint16_t msix_entry; uint64_t msix_addr; uint32_t msix_data; -} QVirtQueuePCI; -extern const QVirtioBus qvirtio_pci; + /* VIRTIO 1.0 */ + uint64_t notify_offset; +} QVirtQueuePCI; void virtio_pci_init(QVirtioPCIDevice *dev, QPCIBus *bus, QPCIAddress * addr); QVirtioPCIDevice *virtio_pci_new(QPCIBus *bus, QPCIAddress * addr); @@ -53,4 +75,12 @@ void qvirtio_pci_set_msix_configuration_vector(QVirtioPCIDevice *d, QGuestAllocator *alloc, uint16_t entry); void qvirtqueue_pci_msix_setup(QVirtioPCIDevice *d, QVirtQueuePCI *vqpci, QGuestAllocator *alloc, uint16_t entry); + +/* Used by Legacy and Modern virtio-pci code */ +QVirtQueue *qvirtio_pci_virtqueue_setup_common(QVirtioDevice *d, + QGuestAllocator *alloc, + uint16_t index); +void qvirtio_pci_virtqueue_cleanup_common(QVirtQueue *vq, + QGuestAllocator *alloc); + #endif diff --git a/tests/libqos/virtio.c b/tests/libqos/virtio.c index 0ae9956fc8..9aa360620c 100644 --- a/tests/libqos/virtio.c +++ b/tests/libqos/virtio.c @@ -8,45 +8,122 @@ */ #include "qemu/osdep.h" +#include "qemu/bswap.h" #include "libqtest.h" #include "libqos/virtio.h" #include "standard-headers/linux/virtio_config.h" #include "standard-headers/linux/virtio_ring.h" +/* + * qtest_readX/writeX() functions transfer host endian from/to guest endian. + * This works great for Legacy VIRTIO devices where we need guest endian + * accesses. For VIRTIO 1.0 the vring is little-endian so the automatic guest + * endianness conversion is not wanted. + * + * The following qvirtio_readX/writeX() functions handle Legacy and VIRTIO 1.0 + * accesses seamlessly. + */ +static uint16_t qvirtio_readw(QVirtioDevice *d, QTestState *qts, uint64_t addr) +{ + uint16_t val = qtest_readw(qts, addr); + + if (d->features & (1ull << VIRTIO_F_VERSION_1) && qtest_big_endian(qts)) { + val = bswap16(val); + } + return val; +} + +static uint32_t qvirtio_readl(QVirtioDevice *d, QTestState *qts, uint64_t addr) +{ + uint32_t val = qtest_readl(qts, addr); + + if (d->features & (1ull << VIRTIO_F_VERSION_1) && qtest_big_endian(qts)) { + val = bswap32(val); + } + return val; +} + +static void qvirtio_writew(QVirtioDevice *d, QTestState *qts, + uint64_t addr, uint16_t val) +{ + if (d->features & (1ull << VIRTIO_F_VERSION_1) && qtest_big_endian(qts)) { + val = bswap16(val); + } + qtest_writew(qts, addr, val); +} + +static void qvirtio_writel(QVirtioDevice *d, QTestState *qts, + uint64_t addr, uint32_t val) +{ + if (d->features & (1ull << VIRTIO_F_VERSION_1) && qtest_big_endian(qts)) { + val = bswap32(val); + } + qtest_writel(qts, addr, val); +} + +static void qvirtio_writeq(QVirtioDevice *d, QTestState *qts, + uint64_t addr, uint64_t val) +{ + if (d->features & (1ull << VIRTIO_F_VERSION_1) && qtest_big_endian(qts)) { + val = bswap64(val); + } + qtest_writeq(qts, addr, val); +} + uint8_t qvirtio_config_readb(QVirtioDevice *d, uint64_t addr) { + g_assert_true(d->features_negotiated); return d->bus->config_readb(d, addr); } uint16_t qvirtio_config_readw(QVirtioDevice *d, uint64_t addr) { + g_assert_true(d->features_negotiated); return d->bus->config_readw(d, addr); } uint32_t qvirtio_config_readl(QVirtioDevice *d, uint64_t addr) { + g_assert_true(d->features_negotiated); return d->bus->config_readl(d, addr); } uint64_t qvirtio_config_readq(QVirtioDevice *d, uint64_t addr) { + g_assert_true(d->features_negotiated); return d->bus->config_readq(d, addr); } -uint32_t qvirtio_get_features(QVirtioDevice *d) +uint64_t qvirtio_get_features(QVirtioDevice *d) { return d->bus->get_features(d); } -void qvirtio_set_features(QVirtioDevice *d, uint32_t features) +void qvirtio_set_features(QVirtioDevice *d, uint64_t features) { d->features = features; d->bus->set_features(d, features); + + /* + * This could be a separate function for drivers that want to access + * configuration space before setting FEATURES_OK, but no existing users + * need that and it's less code for callers if this is done implicitly. + */ + if (features & (1ull << VIRTIO_F_VERSION_1)) { + uint8_t status = d->bus->get_status(d) | + VIRTIO_CONFIG_S_FEATURES_OK; + + d->bus->set_status(d, status); + g_assert_cmphex(d->bus->get_status(d), ==, status); + } + + d->features_negotiated = true; } QVirtQueue *qvirtqueue_setup(QVirtioDevice *d, QGuestAllocator *alloc, uint16_t index) { + g_assert_true(d->features_negotiated); return d->bus->virtqueue_setup(d, alloc, index); } @@ -60,6 +137,7 @@ void qvirtio_reset(QVirtioDevice *d) { d->bus->set_status(d, 0); g_assert_cmphex(d->bus->get_status(d), ==, 0); + d->features_negotiated = false; } void qvirtio_set_acknowledge(QVirtioDevice *d) @@ -79,7 +157,9 @@ void qvirtio_set_driver_ok(QVirtioDevice *d) { d->bus->set_status(d, d->bus->get_status(d) | VIRTIO_CONFIG_S_DRIVER_OK); g_assert_cmphex(d->bus->get_status(d), ==, VIRTIO_CONFIG_S_DRIVER_OK | - VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_ACKNOWLEDGE); + VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_ACKNOWLEDGE | + (d->features & (1ull << VIRTIO_F_VERSION_1) ? + VIRTIO_CONFIG_S_FEATURES_OK : 0)); } void qvirtio_wait_queue_isr(QTestState *qts, QVirtioDevice *d, @@ -166,23 +246,23 @@ void qvring_init(QTestState *qts, const QGuestAllocator *alloc, QVirtQueue *vq, for (i = 0; i < vq->size - 1; i++) { /* vq->desc[i].addr */ - qtest_writeq(qts, vq->desc + (16 * i), 0); + qvirtio_writeq(vq->vdev, qts, vq->desc + (16 * i), 0); /* vq->desc[i].next */ - qtest_writew(qts, vq->desc + (16 * i) + 14, i + 1); + qvirtio_writew(vq->vdev, qts, vq->desc + (16 * i) + 14, i + 1); } /* vq->avail->flags */ - qtest_writew(qts, vq->avail, 0); + qvirtio_writew(vq->vdev, qts, vq->avail, 0); /* vq->avail->idx */ - qtest_writew(qts, vq->avail + 2, 0); + qvirtio_writew(vq->vdev, qts, vq->avail + 2, 0); /* vq->avail->used_event */ - qtest_writew(qts, vq->avail + 4 + (2 * vq->size), 0); + qvirtio_writew(vq->vdev, qts, vq->avail + 4 + (2 * vq->size), 0); /* vq->used->flags */ - qtest_writew(qts, vq->used, 0); + qvirtio_writew(vq->vdev, qts, vq->used, 0); /* vq->used->avail_event */ - qtest_writew(qts, vq->used + 2 + sizeof(struct vring_used_elem) * vq->size, - 0); + qvirtio_writew(vq->vdev, qts, vq->used + 2 + + sizeof(struct vring_used_elem) * vq->size, 0); } QVRingIndirectDesc *qvring_indirect_desc_setup(QTestState *qs, QVirtioDevice *d, @@ -198,35 +278,39 @@ QVRingIndirectDesc *qvring_indirect_desc_setup(QTestState *qs, QVirtioDevice *d, for (i = 0; i < elem - 1; ++i) { /* indirect->desc[i].addr */ - qtest_writeq(qs, indirect->desc + (16 * i), 0); + qvirtio_writeq(d, qs, indirect->desc + (16 * i), 0); /* indirect->desc[i].flags */ - qtest_writew(qs, indirect->desc + (16 * i) + 12, VRING_DESC_F_NEXT); + qvirtio_writew(d, qs, indirect->desc + (16 * i) + 12, + VRING_DESC_F_NEXT); /* indirect->desc[i].next */ - qtest_writew(qs, indirect->desc + (16 * i) + 14, i + 1); + qvirtio_writew(d, qs, indirect->desc + (16 * i) + 14, i + 1); } return indirect; } -void qvring_indirect_desc_add(QTestState *qts, QVRingIndirectDesc *indirect, +void qvring_indirect_desc_add(QVirtioDevice *d, QTestState *qts, + QVRingIndirectDesc *indirect, uint64_t data, uint32_t len, bool write) { uint16_t flags; g_assert_cmpint(indirect->index, <, indirect->elem); - flags = qtest_readw(qts, indirect->desc + (16 * indirect->index) + 12); + flags = qvirtio_readw(d, qts, indirect->desc + + (16 * indirect->index) + 12); if (write) { flags |= VRING_DESC_F_WRITE; } /* indirect->desc[indirect->index].addr */ - qtest_writeq(qts, indirect->desc + (16 * indirect->index), data); + qvirtio_writeq(d, qts, indirect->desc + (16 * indirect->index), data); /* indirect->desc[indirect->index].len */ - qtest_writel(qts, indirect->desc + (16 * indirect->index) + 8, len); + qvirtio_writel(d, qts, indirect->desc + (16 * indirect->index) + 8, len); /* indirect->desc[indirect->index].flags */ - qtest_writew(qts, indirect->desc + (16 * indirect->index) + 12, flags); + qvirtio_writew(d, qts, indirect->desc + (16 * indirect->index) + 12, + flags); indirect->index++; } @@ -246,11 +330,11 @@ uint32_t qvirtqueue_add(QTestState *qts, QVirtQueue *vq, uint64_t data, } /* vq->desc[vq->free_head].addr */ - qtest_writeq(qts, vq->desc + (16 * vq->free_head), data); + qvirtio_writeq(vq->vdev, qts, vq->desc + (16 * vq->free_head), data); /* vq->desc[vq->free_head].len */ - qtest_writel(qts, vq->desc + (16 * vq->free_head) + 8, len); + qvirtio_writel(vq->vdev, qts, vq->desc + (16 * vq->free_head) + 8, len); /* vq->desc[vq->free_head].flags */ - qtest_writew(qts, vq->desc + (16 * vq->free_head) + 12, flags); + qvirtio_writew(vq->vdev, qts, vq->desc + (16 * vq->free_head) + 12, flags); return vq->free_head++; /* Return and increase, in this order */ } @@ -265,13 +349,14 @@ uint32_t qvirtqueue_add_indirect(QTestState *qts, QVirtQueue *vq, vq->num_free--; /* vq->desc[vq->free_head].addr */ - qtest_writeq(qts, vq->desc + (16 * vq->free_head), indirect->desc); + qvirtio_writeq(vq->vdev, qts, vq->desc + (16 * vq->free_head), + indirect->desc); /* vq->desc[vq->free_head].len */ - qtest_writel(qts, vq->desc + (16 * vq->free_head) + 8, - sizeof(struct vring_desc) * indirect->elem); + qvirtio_writel(vq->vdev, qts, vq->desc + (16 * vq->free_head) + 8, + sizeof(struct vring_desc) * indirect->elem); /* vq->desc[vq->free_head].flags */ - qtest_writew(qts, vq->desc + (16 * vq->free_head) + 12, - VRING_DESC_F_INDIRECT); + qvirtio_writew(vq->vdev, qts, vq->desc + (16 * vq->free_head) + 12, + VRING_DESC_F_INDIRECT); return vq->free_head++; /* Return and increase, in this order */ } @@ -280,21 +365,21 @@ void qvirtqueue_kick(QTestState *qts, QVirtioDevice *d, QVirtQueue *vq, uint32_t free_head) { /* vq->avail->idx */ - uint16_t idx = qtest_readw(qts, vq->avail + 2); + uint16_t idx = qvirtio_readw(d, qts, vq->avail + 2); /* vq->used->flags */ uint16_t flags; /* vq->used->avail_event */ uint16_t avail_event; /* vq->avail->ring[idx % vq->size] */ - qtest_writew(qts, vq->avail + 4 + (2 * (idx % vq->size)), free_head); + qvirtio_writew(d, qts, vq->avail + 4 + (2 * (idx % vq->size)), free_head); /* vq->avail->idx */ - qtest_writew(qts, vq->avail + 2, idx + 1); + qvirtio_writew(d, qts, vq->avail + 2, idx + 1); /* Must read after idx is updated */ - flags = qtest_readw(qts, vq->avail); - avail_event = qtest_readw(qts, vq->used + 4 + - sizeof(struct vring_used_elem) * vq->size); + flags = qvirtio_readw(d, qts, vq->avail); + avail_event = qvirtio_readw(d, qts, vq->used + 4 + + sizeof(struct vring_used_elem) * vq->size); /* < 1 because we add elements to avail queue one by one */ if ((flags & VRING_USED_F_NO_NOTIFY) == 0 && @@ -319,7 +404,8 @@ bool qvirtqueue_get_buf(QTestState *qts, QVirtQueue *vq, uint32_t *desc_idx, uint16_t idx; uint64_t elem_addr, addr; - idx = qtest_readw(qts, vq->used + offsetof(struct vring_used, idx)); + idx = qvirtio_readw(vq->vdev, qts, + vq->used + offsetof(struct vring_used, idx)); if (idx == vq->last_used_idx) { return false; } @@ -331,12 +417,12 @@ bool qvirtqueue_get_buf(QTestState *qts, QVirtQueue *vq, uint32_t *desc_idx, if (desc_idx) { addr = elem_addr + offsetof(struct vring_used_elem, id); - *desc_idx = qtest_readl(qts, addr); + *desc_idx = qvirtio_readl(vq->vdev, qts, addr); } if (len) { addr = elem_addr + offsetof(struct vring_used_elem, len); - *len = qtest_readw(qts, addr); + *len = qvirtio_readw(vq->vdev, qts, addr); } vq->last_used_idx++; @@ -348,7 +434,7 @@ void qvirtqueue_set_used_event(QTestState *qts, QVirtQueue *vq, uint16_t idx) g_assert(vq->event); /* vq->avail->used_event */ - qtest_writew(qts, vq->avail + 4 + (2 * vq->size), idx); + qvirtio_writew(vq->vdev, qts, vq->avail + 4 + (2 * vq->size), idx); } void qvirtio_start_device(QVirtioDevice *vdev) diff --git a/tests/libqos/virtio.h b/tests/libqos/virtio.h index 2cb2448f46..529ef7555a 100644 --- a/tests/libqos/virtio.h +++ b/tests/libqos/virtio.h @@ -13,7 +13,7 @@ #include "libqos/malloc.h" #include "standard-headers/linux/virtio_ring.h" -#define QVIRTIO_F_BAD_FEATURE 0x40000000 +#define QVIRTIO_F_BAD_FEATURE 0x40000000ull typedef struct QVirtioBus QVirtioBus; @@ -23,9 +23,11 @@ typedef struct QVirtioDevice { uint16_t device_type; uint64_t features; bool big_endian; + bool features_negotiated; } QVirtioDevice; typedef struct QVirtQueue { + QVirtioDevice *vdev; uint64_t desc; /* This points to an array of struct vring_desc */ uint64_t avail; /* This points to a struct vring_avail */ uint64_t used; /* This points to a struct vring_used */ @@ -52,13 +54,13 @@ struct QVirtioBus { uint64_t (*config_readq)(QVirtioDevice *d, uint64_t addr); /* Get features of the device */ - uint32_t (*get_features)(QVirtioDevice *d); + uint64_t (*get_features)(QVirtioDevice *d); /* Set features of the device */ - void (*set_features)(QVirtioDevice *d, uint32_t features); + void (*set_features)(QVirtioDevice *d, uint64_t features); /* Get features of the guest */ - uint32_t (*get_guest_features)(QVirtioDevice *d); + uint64_t (*get_guest_features)(QVirtioDevice *d); /* Get status of the device */ uint8_t (*get_status)(QVirtioDevice *d); @@ -79,7 +81,7 @@ struct QVirtioBus { uint16_t (*get_queue_size)(QVirtioDevice *d); /* Set the address of the selected queue */ - void (*set_queue_address)(QVirtioDevice *d, uint32_t pfn); + void (*set_queue_address)(QVirtioDevice *d, QVirtQueue *vq); /* Setup the virtqueue specified by index */ QVirtQueue *(*virtqueue_setup)(QVirtioDevice *d, QGuestAllocator *alloc, @@ -103,8 +105,8 @@ uint8_t qvirtio_config_readb(QVirtioDevice *d, uint64_t addr); uint16_t qvirtio_config_readw(QVirtioDevice *d, uint64_t addr); uint32_t qvirtio_config_readl(QVirtioDevice *d, uint64_t addr); uint64_t qvirtio_config_readq(QVirtioDevice *d, uint64_t addr); -uint32_t qvirtio_get_features(QVirtioDevice *d); -void qvirtio_set_features(QVirtioDevice *d, uint32_t features); +uint64_t qvirtio_get_features(QVirtioDevice *d); +void qvirtio_set_features(QVirtioDevice *d, uint64_t features); bool qvirtio_is_big_endian(QVirtioDevice *d); void qvirtio_reset(QVirtioDevice *d); @@ -134,7 +136,8 @@ void qvring_init(QTestState *qts, const QGuestAllocator *alloc, QVirtQueue *vq, QVRingIndirectDesc *qvring_indirect_desc_setup(QTestState *qs, QVirtioDevice *d, QGuestAllocator *alloc, uint16_t elem); -void qvring_indirect_desc_add(QTestState *qts, QVRingIndirectDesc *indirect, +void qvring_indirect_desc_add(QVirtioDevice *d, QTestState *qts, + QVRingIndirectDesc *indirect, uint64_t data, uint32_t len, bool write); uint32_t qvirtqueue_add(QTestState *qts, QVirtQueue *vq, uint64_t data, uint32_t len, bool write, bool next); diff --git a/tests/qemu-iotests/083 b/tests/qemu-iotests/083 index b270550d3e..10fdfc8ebb 100755 --- a/tests/qemu-iotests/083 +++ b/tests/qemu-iotests/083 @@ -28,7 +28,7 @@ status=1 # failure is the default! _cleanup() { - rm -f nbd.sock + rm -f "$SOCK_DIR/nbd.sock" rm -f nbd-fault-injector.out rm -f nbd-fault-injector.conf } @@ -80,10 +80,10 @@ EOF if [ "$proto" = "tcp" ]; then nbd_addr="127.0.0.1:0" else - nbd_addr="$TEST_DIR/nbd.sock" + nbd_addr="$SOCK_DIR/nbd.sock" fi - rm -f "$TEST_DIR/nbd.sock" + rm -f "$SOCK_DIR/nbd.sock" echo > "$TEST_DIR/nbd-fault-injector.out" $PYTHON nbd-fault-injector.py $extra_args "$nbd_addr" "$TEST_DIR/nbd-fault-injector.conf" >"$TEST_DIR/nbd-fault-injector.out" 2>&1 & diff --git a/tests/qemu-iotests/083.out b/tests/qemu-iotests/083.out index eee6dd1379..2090ee693c 100644 --- a/tests/qemu-iotests/083.out +++ b/tests/qemu-iotests/083.out @@ -110,43 +110,43 @@ read failed: Input/output error === Check disconnect before neg1 === -qemu-io: can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock +qemu-io: can't open device nbd+unix:///foo?socket=SOCK_DIR/nbd.sock === Check disconnect after neg1 === -qemu-io: can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock +qemu-io: can't open device nbd+unix:///foo?socket=SOCK_DIR/nbd.sock === Check disconnect 8 neg1 === -qemu-io: can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock +qemu-io: can't open device nbd+unix:///foo?socket=SOCK_DIR/nbd.sock === Check disconnect 16 neg1 === -qemu-io: can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock +qemu-io: can't open device nbd+unix:///foo?socket=SOCK_DIR/nbd.sock === Check disconnect before export === -qemu-io: can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock +qemu-io: can't open device nbd+unix:///foo?socket=SOCK_DIR/nbd.sock === Check disconnect after export === -qemu-io: can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock +qemu-io: can't open device nbd+unix:///foo?socket=SOCK_DIR/nbd.sock === Check disconnect 4 export === -qemu-io: can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock +qemu-io: can't open device nbd+unix:///foo?socket=SOCK_DIR/nbd.sock === Check disconnect 12 export === -qemu-io: can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock +qemu-io: can't open device nbd+unix:///foo?socket=SOCK_DIR/nbd.sock === Check disconnect 16 export === -qemu-io: can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock +qemu-io: can't open device nbd+unix:///foo?socket=SOCK_DIR/nbd.sock === Check disconnect before neg2 === -qemu-io: can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock +qemu-io: can't open device nbd+unix:///foo?socket=SOCK_DIR/nbd.sock === Check disconnect after neg2 === @@ -154,11 +154,11 @@ read failed: Input/output error === Check disconnect 8 neg2 === -qemu-io: can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock +qemu-io: can't open device nbd+unix:///foo?socket=SOCK_DIR/nbd.sock === Check disconnect 10 neg2 === -qemu-io: can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock +qemu-io: can't open device nbd+unix:///foo?socket=SOCK_DIR/nbd.sock === Check disconnect before request === @@ -195,23 +195,23 @@ read 512/512 bytes at offset 0 === Check disconnect before neg-classic === -qemu-io: can't open device nbd+unix:///?socket=TEST_DIR/nbd.sock +qemu-io: can't open device nbd+unix:///?socket=SOCK_DIR/nbd.sock === Check disconnect 8 neg-classic === -qemu-io: can't open device nbd+unix:///?socket=TEST_DIR/nbd.sock +qemu-io: can't open device nbd+unix:///?socket=SOCK_DIR/nbd.sock === Check disconnect 16 neg-classic === -qemu-io: can't open device nbd+unix:///?socket=TEST_DIR/nbd.sock +qemu-io: can't open device nbd+unix:///?socket=SOCK_DIR/nbd.sock === Check disconnect 24 neg-classic === -qemu-io: can't open device nbd+unix:///?socket=TEST_DIR/nbd.sock +qemu-io: can't open device nbd+unix:///?socket=SOCK_DIR/nbd.sock === Check disconnect 28 neg-classic === -qemu-io: can't open device nbd+unix:///?socket=TEST_DIR/nbd.sock +qemu-io: can't open device nbd+unix:///?socket=SOCK_DIR/nbd.sock === Check disconnect after neg-classic === diff --git a/tests/qemu-iotests/093 b/tests/qemu-iotests/093 index 3c4f5173ce..f03fa24a07 100755 --- a/tests/qemu-iotests/093 +++ b/tests/qemu-iotests/093 @@ -24,7 +24,7 @@ import iotests nsec_per_sec = 1000000000 class ThrottleTestCase(iotests.QMPTestCase): - test_img = "null-aio://" + test_driver = "null-aio" max_drives = 3 def blockstats(self, device): @@ -35,10 +35,14 @@ class ThrottleTestCase(iotests.QMPTestCase): return stat['rd_bytes'], stat['rd_operations'], stat['wr_bytes'], stat['wr_operations'] raise Exception("Device not found for blockstats: %s" % device) + def required_drivers(self): + return [self.test_driver] + + @iotests.skip_if_unsupported(required_drivers) def setUp(self): self.vm = iotests.VM() for i in range(0, self.max_drives): - self.vm.add_drive(self.test_img, "file.read-zeroes=on") + self.vm.add_drive(self.test_driver + "://", "file.read-zeroes=on") self.vm.launch() def tearDown(self): @@ -264,16 +268,15 @@ class ThrottleTestCase(iotests.QMPTestCase): self.assertEqual(self.blockstats('drive1')[0], 4096) class ThrottleTestCoroutine(ThrottleTestCase): - test_img = "null-co://" + test_driver = "null-co" class ThrottleTestGroupNames(iotests.QMPTestCase): - test_img = "null-aio://" max_drives = 3 def setUp(self): self.vm = iotests.VM() for i in range(0, self.max_drives): - self.vm.add_drive(self.test_img, + self.vm.add_drive("null-co://", "throttling.iops-total=100,file.read-zeroes=on") self.vm.launch() @@ -376,10 +379,10 @@ class ThrottleTestRemovableMedia(iotests.QMPTestCase): def test_removable_media(self): # Add a couple of dummy nodes named cd0 and cd1 - result = self.vm.qmp("blockdev-add", driver="null-aio", + result = self.vm.qmp("blockdev-add", driver="null-co", read_zeroes=True, node_name="cd0") self.assert_qmp(result, 'return', {}) - result = self.vm.qmp("blockdev-add", driver="null-aio", + result = self.vm.qmp("blockdev-add", driver="null-co", read_zeroes=True, node_name="cd1") self.assert_qmp(result, 'return', {}) @@ -426,4 +429,6 @@ class ThrottleTestRemovableMedia(iotests.QMPTestCase): if __name__ == '__main__': + if 'null-co' not in iotests.supported_formats(): + iotests.notrun('null-co driver support missing') iotests.main(supported_fmts=["raw"]) diff --git a/tests/qemu-iotests/118 b/tests/qemu-iotests/118 index ea0b326ae0..e20080e9a6 100755 --- a/tests/qemu-iotests/118 +++ b/tests/qemu-iotests/118 @@ -446,6 +446,7 @@ class TestChangeReadOnly(ChangeBaseClass): self.assert_qmp(result, 'return[0]/inserted/ro', True) self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img) + @iotests.skip_if_user_is_root def test_rw_ro_retain(self): os.chmod(new_img, 0o444) self.vm.add_drive(old_img, 'media=disk', 'none') @@ -530,6 +531,7 @@ class TestChangeReadOnly(ChangeBaseClass): self.assert_qmp(result, 'return[0]/inserted/ro', True) self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img) + @iotests.skip_if_user_is_root def test_make_ro_rw(self): os.chmod(new_img, 0o444) self.vm.add_drive(old_img, 'media=disk', 'none') @@ -571,6 +573,7 @@ class TestChangeReadOnly(ChangeBaseClass): self.assert_qmp(result, 'return[0]/inserted/ro', True) self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img) + @iotests.skip_if_user_is_root def test_make_ro_rw_by_retain(self): os.chmod(new_img, 0o444) self.vm.add_drive(old_img, 'media=disk', 'none') diff --git a/tests/qemu-iotests/136 b/tests/qemu-iotests/136 index a46a7b7630..012ea111ac 100755 --- a/tests/qemu-iotests/136 +++ b/tests/qemu-iotests/136 @@ -30,7 +30,7 @@ bad_offset = bad_sector * 512 blkdebug_file = os.path.join(iotests.test_dir, 'blkdebug.conf') class BlockDeviceStatsTestCase(iotests.QMPTestCase): - test_img = "null-aio://" + test_driver = "null-aio" total_rd_bytes = 0 total_rd_ops = 0 total_wr_bytes = 0 @@ -67,6 +67,10 @@ sector = "%d" ''' % (bad_sector, bad_sector)) file.close() + def required_drivers(self): + return [self.test_driver] + + @iotests.skip_if_unsupported(required_drivers) def setUp(self): drive_args = [] drive_args.append("stats-intervals.0=%d" % interval_length) @@ -76,8 +80,8 @@ sector = "%d" (self.account_failed and "on" or "off")) drive_args.append("file.image.read-zeroes=on") self.create_blkdebug_file() - self.vm = iotests.VM().add_drive('blkdebug:%s:%s' % - (blkdebug_file, self.test_img), + self.vm = iotests.VM().add_drive('blkdebug:%s:%s://' % + (blkdebug_file, self.test_driver), ','.join(drive_args)) self.vm.launch() # Set an initial value for the clock @@ -337,7 +341,9 @@ class BlockDeviceStatsTestAccountBoth(BlockDeviceStatsTestCase): account_failed = True class BlockDeviceStatsTestCoroutine(BlockDeviceStatsTestCase): - test_img = "null-co://" + test_driver = "null-co" if __name__ == '__main__': + if 'null-co' not in iotests.supported_formats(): + iotests.notrun('null-co driver support missing') iotests.main(supported_fmts=["raw"]) diff --git a/tests/qemu-iotests/140 b/tests/qemu-iotests/140 index b965b1dd5d..8d2ce5d9e3 100755 --- a/tests/qemu-iotests/140 +++ b/tests/qemu-iotests/140 @@ -34,7 +34,7 @@ _cleanup() { _cleanup_qemu _cleanup_test_img - rm -f "$TEST_DIR/nbd" + rm -f "$SOCK_DIR/nbd" } trap "_cleanup; exit \$status" 0 1 2 3 15 @@ -69,7 +69,7 @@ _send_qemu_cmd $QEMU_HANDLE \ _send_qemu_cmd $QEMU_HANDLE \ "{ 'execute': 'nbd-server-start', 'arguments': { 'addr': { 'type': 'unix', - 'data': { 'path': '$TEST_DIR/nbd' }}}}" \ + 'data': { 'path': '$SOCK_DIR/nbd' }}}}" \ 'return' _send_qemu_cmd $QEMU_HANDLE \ @@ -78,7 +78,7 @@ _send_qemu_cmd $QEMU_HANDLE \ 'return' $QEMU_IO_PROG -f raw -r -c 'read -P 42 0 64k' \ - "nbd+unix:///drv?socket=$TEST_DIR/nbd" 2>&1 \ + "nbd+unix:///drv?socket=$SOCK_DIR/nbd" 2>&1 \ | _filter_qemu_io | _filter_nbd _send_qemu_cmd $QEMU_HANDLE \ @@ -87,7 +87,7 @@ _send_qemu_cmd $QEMU_HANDLE \ 'return' $QEMU_IO_PROG -f raw -r -c close \ - "nbd+unix:///drv?socket=$TEST_DIR/nbd" 2>&1 \ + "nbd+unix:///drv?socket=$SOCK_DIR/nbd" 2>&1 \ | _filter_qemu_io | _filter_nbd _send_qemu_cmd $QEMU_HANDLE \ diff --git a/tests/qemu-iotests/140.out b/tests/qemu-iotests/140.out index 67fe44a3e3..2511eb7369 100644 --- a/tests/qemu-iotests/140.out +++ b/tests/qemu-iotests/140.out @@ -8,7 +8,7 @@ wrote 65536/65536 bytes at offset 0 read 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) {"return": {}} -qemu-io: can't open device nbd+unix:///drv?socket=TEST_DIR/nbd: Requested export not available +qemu-io: can't open device nbd+unix:///drv?socket=SOCK_DIR/nbd: Requested export not available server reported: export 'drv' not present {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} diff --git a/tests/qemu-iotests/143 b/tests/qemu-iotests/143 index 92249ac8da..f649b36195 100755 --- a/tests/qemu-iotests/143 +++ b/tests/qemu-iotests/143 @@ -29,7 +29,7 @@ status=1 # failure is the default! _cleanup() { _cleanup_qemu - rm -f "$TEST_DIR/nbd" + rm -f "$SOCK_DIR/nbd" } trap "_cleanup; exit \$status" 0 1 2 3 15 @@ -51,12 +51,12 @@ _send_qemu_cmd $QEMU_HANDLE \ _send_qemu_cmd $QEMU_HANDLE \ "{ 'execute': 'nbd-server-start', 'arguments': { 'addr': { 'type': 'unix', - 'data': { 'path': '$TEST_DIR/nbd' }}}}" \ + 'data': { 'path': '$SOCK_DIR/nbd' }}}}" \ 'return' # This should just result in a client error, not in the server crashing $QEMU_IO_PROG -f raw -c quit \ - "nbd+unix:///no_such_export?socket=$TEST_DIR/nbd" 2>&1 \ + "nbd+unix:///no_such_export?socket=$SOCK_DIR/nbd" 2>&1 \ | _filter_qemu_io | _filter_nbd _send_qemu_cmd $QEMU_HANDLE \ diff --git a/tests/qemu-iotests/143.out b/tests/qemu-iotests/143.out index ee71b5aa42..037d34a409 100644 --- a/tests/qemu-iotests/143.out +++ b/tests/qemu-iotests/143.out @@ -1,7 +1,7 @@ QA output created by 143 {"return": {}} {"return": {}} -qemu-io: can't open device nbd+unix:///no_such_export?socket=TEST_DIR/nbd: Requested export not available +qemu-io: can't open device nbd+unix:///no_such_export?socket=SOCK_DIR/nbd: Requested export not available server reported: export 'no_such_export' not present {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} diff --git a/tests/qemu-iotests/147 b/tests/qemu-iotests/147 index ab8480b9a4..03fc2fabcf 100755 --- a/tests/qemu-iotests/147 +++ b/tests/qemu-iotests/147 @@ -32,7 +32,7 @@ NBD_IPV6_PORT_START = NBD_PORT_END NBD_IPV6_PORT_END = NBD_IPV6_PORT_START + 1024 test_img = os.path.join(iotests.test_dir, 'test.img') -unix_socket = os.path.join(iotests.test_dir, 'nbd.socket') +unix_socket = os.path.join(iotests.sock_dir, 'nbd.socket') def flatten_sock_addr(crumpled_address): diff --git a/tests/qemu-iotests/181 b/tests/qemu-iotests/181 index e317e63422..378c2899d1 100755 --- a/tests/qemu-iotests/181 +++ b/tests/qemu-iotests/181 @@ -26,7 +26,7 @@ echo "QA output created by $seq" status=1 # failure is the default! -MIG_SOCKET="${TEST_DIR}/migrate" +MIG_SOCKET="${SOCK_DIR}/migrate" _cleanup() { diff --git a/tests/qemu-iotests/182 b/tests/qemu-iotests/182 index 7f494eb9bb..1ccb850055 100755 --- a/tests/qemu-iotests/182 +++ b/tests/qemu-iotests/182 @@ -31,7 +31,7 @@ _cleanup() { _cleanup_test_img rm -f "$TEST_IMG.overlay" - rm -f "$TEST_DIR/nbd.socket" + rm -f "$SOCK_DIR/nbd.socket" } trap "_cleanup; exit \$status" 0 1 2 3 15 @@ -133,7 +133,7 @@ success_or_failure=y _send_qemu_cmd $QEMU_HANDLE \ 'addr': { 'type': 'unix', 'data': { - 'path': '$TEST_DIR/nbd.socket' + 'path': '$SOCK_DIR/nbd.socket' } } } }" \ 'return' \ 'error' diff --git a/tests/qemu-iotests/183 b/tests/qemu-iotests/183 index 04fb344d08..bced83fae0 100755 --- a/tests/qemu-iotests/183 +++ b/tests/qemu-iotests/183 @@ -26,7 +26,7 @@ echo "QA output created by $seq" status=1 # failure is the default! -MIG_SOCKET="${TEST_DIR}/migrate" +MIG_SOCKET="${SOCK_DIR}/migrate" _cleanup() { diff --git a/tests/qemu-iotests/192 b/tests/qemu-iotests/192 index 034432272f..d2ba55dd90 100755 --- a/tests/qemu-iotests/192 +++ b/tests/qemu-iotests/192 @@ -31,7 +31,7 @@ _cleanup() { _cleanup_qemu _cleanup_test_img - rm -f "$TEST_DIR/nbd" + rm -f "$SOCK_DIR/nbd" } trap "_cleanup; exit \$status" 0 1 2 3 15 @@ -66,7 +66,7 @@ else QEMU_COMM_TIMEOUT=1 fi -_send_qemu_cmd $h "nbd_server_start unix:$TEST_DIR/nbd" "(qemu)" +_send_qemu_cmd $h "nbd_server_start unix:$SOCK_DIR/nbd" "(qemu)" _send_qemu_cmd $h "nbd_server_add -w drive0" "(qemu)" _send_qemu_cmd $h "q" "(qemu)" diff --git a/tests/qemu-iotests/192.out b/tests/qemu-iotests/192.out index 1e0be4c4d7..b9429dbe36 100644 --- a/tests/qemu-iotests/192.out +++ b/tests/qemu-iotests/192.out @@ -1,7 +1,7 @@ QA output created by 192 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 QEMU X.Y.Z monitor - type 'help' for more information -(qemu) nbd_server_start unix:TEST_DIR/nbd +(qemu) nbd_server_start unix:SOCK_DIR/nbd (qemu) nbd_server_add -w drive0 (qemu) q *** done diff --git a/tests/qemu-iotests/194 b/tests/qemu-iotests/194 index d746ab1e21..72e47e8833 100755 --- a/tests/qemu-iotests/194 +++ b/tests/qemu-iotests/194 @@ -26,8 +26,8 @@ iotests.verify_platform(['linux']) with iotests.FilePath('source.img') as source_img_path, \ iotests.FilePath('dest.img') as dest_img_path, \ - iotests.FilePath('migration.sock') as migration_sock_path, \ - iotests.FilePath('nbd.sock') as nbd_sock_path, \ + iotests.FilePaths(['migration.sock', 'nbd.sock'], iotests.sock_dir) as \ + [migration_sock_path, nbd_sock_path], \ iotests.VM('source') as source_vm, \ iotests.VM('dest') as dest_vm: diff --git a/tests/qemu-iotests/201 b/tests/qemu-iotests/201 index 7abf740fe4..86fa37e714 100755 --- a/tests/qemu-iotests/201 +++ b/tests/qemu-iotests/201 @@ -24,7 +24,7 @@ echo "QA output created by $seq" status=1 # failure is the default! -MIG_SOCKET="${TEST_DIR}/migrate" +MIG_SOCKET="${SOCK_DIR}/migrate" # get standard environment, filters and checks . ./common.rc diff --git a/tests/qemu-iotests/205 b/tests/qemu-iotests/205 index 76f6c5fa2b..4bb2c21e8b 100755 --- a/tests/qemu-iotests/205 +++ b/tests/qemu-iotests/205 @@ -24,7 +24,7 @@ import iotests import time from iotests import qemu_img_create, qemu_io, filter_qemu_io, QemuIoInteractive -nbd_sock = os.path.join(iotests.test_dir, 'nbd_sock') +nbd_sock = os.path.join(iotests.sock_dir, 'nbd_sock') nbd_uri = 'nbd+unix:///exp?socket=' + nbd_sock disk = os.path.join(iotests.test_dir, 'disk') diff --git a/tests/qemu-iotests/208 b/tests/qemu-iotests/208 index 1e202388dc..546eb1de3e 100755 --- a/tests/qemu-iotests/208 +++ b/tests/qemu-iotests/208 @@ -26,7 +26,7 @@ iotests.verify_image_format(supported_fmts=['generic']) with iotests.FilePath('disk.img') as disk_img_path, \ iotests.FilePath('disk-snapshot.img') as disk_snapshot_img_path, \ - iotests.FilePath('nbd.sock') as nbd_sock_path, \ + iotests.FilePath('nbd.sock', iotests.sock_dir) as nbd_sock_path, \ iotests.VM() as vm: img_size = '10M' diff --git a/tests/qemu-iotests/209 b/tests/qemu-iotests/209 index 259e991ec6..e0f464bcbe 100755 --- a/tests/qemu-iotests/209 +++ b/tests/qemu-iotests/209 @@ -24,7 +24,8 @@ from iotests import qemu_img_create, qemu_io, qemu_img_verbose, qemu_nbd, \ iotests.verify_image_format(supported_fmts=['qcow2']) -disk, nbd_sock = file_path('disk', 'nbd-sock') +disk = file_path('disk') +nbd_sock = file_path('nbd-sock', base_dir=iotests.sock_dir) nbd_uri = 'nbd+unix:///exp?socket=' + nbd_sock qemu_img_create('-f', iotests.imgfmt, disk, '1M') diff --git a/tests/qemu-iotests/222 b/tests/qemu-iotests/222 index 0ead56d574..3f9f934ad8 100644 --- a/tests/qemu-iotests/222 +++ b/tests/qemu-iotests/222 @@ -48,7 +48,7 @@ remainder = [("0xd5", "0x108000", "32k"), # Right-end of partial-left [1] with iotests.FilePath('base.img') as base_img_path, \ iotests.FilePath('fleece.img') as fleece_img_path, \ - iotests.FilePath('nbd.sock') as nbd_sock_path, \ + iotests.FilePath('nbd.sock', iotests.sock_dir) as nbd_sock_path, \ iotests.VM() as vm: log('--- Setting up images ---') diff --git a/tests/qemu-iotests/223 b/tests/qemu-iotests/223 index 2ba3d8124b..b5a80e50bb 100755 --- a/tests/qemu-iotests/223 +++ b/tests/qemu-iotests/223 @@ -28,7 +28,7 @@ _cleanup() nbd_server_stop _cleanup_test_img _cleanup_qemu - rm -f "$TEST_DIR/nbd" + rm -f "$SOCK_DIR/nbd" } trap "_cleanup; exit \$status" 0 1 2 3 15 @@ -125,11 +125,11 @@ _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add", "arguments":{"device":"n"}}' "error" # Attempt add without server _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-start", "arguments":{"addr":{"type":"unix", - "data":{"path":"'"$TEST_DIR/nbd"'"}}}}' "return" + "data":{"path":"'"$SOCK_DIR/nbd"'"}}}}' "return" _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-start", "arguments":{"addr":{"type":"unix", - "data":{"path":"'"$TEST_DIR/nbd"1'"}}}}' "error" # Attempt second server -$QEMU_NBD_PROG -L -k "$TEST_DIR/nbd" + "data":{"path":"'"$SOCK_DIR/nbd"1'"}}}}' "error" # Attempt second server +$QEMU_NBD_PROG -L -k "$SOCK_DIR/nbd" _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add", "arguments":{"device":"n", "bitmap":"b"}}' "return" _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add", @@ -145,14 +145,14 @@ _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add", _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add", "arguments":{"device":"n", "name":"n2", "writable":true, "bitmap":"b2"}}' "return" -$QEMU_NBD_PROG -L -k "$TEST_DIR/nbd" +$QEMU_NBD_PROG -L -k "$SOCK_DIR/nbd" echo echo "=== Contrast normal status to large granularity dirty-bitmap ===" echo QEMU_IO_OPTIONS=$QEMU_IO_OPTIONS_NO_FMT -IMG="driver=nbd,export=n,server.type=unix,server.path=$TEST_DIR/nbd" +IMG="driver=nbd,export=n,server.type=unix,server.path=$SOCK_DIR/nbd" $QEMU_IO -r -c 'r -P 0x22 512 512' -c 'r -P 0 512k 512k' -c 'r -P 0x11 1m 1m' \ -c 'r -P 0x33 2m 2m' --image-opts "$IMG" | _filter_qemu_io $QEMU_IMG map --output=json --image-opts \ @@ -164,7 +164,7 @@ echo echo "=== Contrast to small granularity dirty-bitmap ===" echo -IMG="driver=nbd,export=n2,server.type=unix,server.path=$TEST_DIR/nbd" +IMG="driver=nbd,export=n2,server.type=unix,server.path=$SOCK_DIR/nbd" $QEMU_IMG map --output=json --image-opts \ "$IMG,x-dirty-bitmap=qemu:dirty-bitmap:b2" | _filter_qemu_img_map diff --git a/tests/qemu-iotests/240 b/tests/qemu-iotests/240 index f73bc07d80..8b4337b58d 100755 --- a/tests/qemu-iotests/240 +++ b/tests/qemu-iotests/240 @@ -29,7 +29,7 @@ status=1 # failure is the default! _cleanup() { - rm -f "$TEST_DIR/nbd" + rm -f "$SOCK_DIR/nbd" } trap "_cleanup; exit \$status" 0 1 2 3 15 @@ -135,7 +135,7 @@ echo run_qemu <<EOF { "execute": "qmp_capabilities" } { "execute": "blockdev-add", "arguments": {"driver": "null-co", "read-zeroes": true, "node-name": "hd0", "read-only": true}} -{ "execute": "nbd-server-start", "arguments": {"addr":{"type":"unix","data":{"path":"$TEST_DIR/nbd"}}}} +{ "execute": "nbd-server-start", "arguments": {"addr":{"type":"unix","data":{"path":"$SOCK_DIR/nbd"}}}} { "execute": "nbd-server-add", "arguments": {"device":"hd0"}} { "execute": "object-add", "arguments": {"qom-type": "iothread", "id": "iothread0"}} { "execute": "device_add", "arguments": {"id": "scsi0", "driver": "${virtio_scsi}", "iothread": "iothread0"}} diff --git a/tests/qemu-iotests/241 b/tests/qemu-iotests/241 index 58b64ebf41..8dae8d39e4 100755 --- a/tests/qemu-iotests/241 +++ b/tests/qemu-iotests/241 @@ -23,8 +23,6 @@ echo "QA output created by $seq" status=1 # failure is the default! -nbd_unix_socket=$TEST_DIR/test_qemu_nbd_socket - _cleanup() { _cleanup_test_img diff --git a/tests/qemu-iotests/245 b/tests/qemu-iotests/245 index 41218d5f1d..e66a23c5f0 100644 --- a/tests/qemu-iotests/245 +++ b/tests/qemu-iotests/245 @@ -598,7 +598,7 @@ class TestBlockdevReopen(iotests.QMPTestCase): ################## ###### null ###### ################## - opts = {'driver': 'null-aio', 'node-name': 'root', 'size': 1024} + opts = {'driver': 'null-co', 'node-name': 'root', 'size': 1024} result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) self.assert_qmp(result, 'return', {}) diff --git a/tests/qemu-iotests/261 b/tests/qemu-iotests/261 new file mode 100755 index 0000000000..fb96bcfbe2 --- /dev/null +++ b/tests/qemu-iotests/261 @@ -0,0 +1,523 @@ +#!/usr/bin/env bash +# +# Test case for qcow2's handling of extra data in snapshot table entries +# (and more generally, how certain cases of broken snapshot tables are +# handled) +# +# Copyright (C) 2019 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +# creator +owner=mreitz@redhat.com + +seq=$(basename $0) +echo "QA output created by $seq" + +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img + rm -f "$TEST_IMG".v{2,3}.orig + rm -f "$TEST_DIR"/sn{0,1,2}{,-pre,-extra,-post} +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +# This tests qocw2-specific low-level functionality +_supported_fmt qcow2 +_supported_proto file +_supported_os Linux +# (1) We create a v2 image that supports nothing but refcount_bits=16 +# (2) We do some refcount management on our own which expects +# refcount_bits=16 +_unsupported_imgopts 'refcount_bits=\([^1]\|.\([^6]\|$\)\)' + +# Parameters: +# $1: image filename +# $2: snapshot table entry offset in the image +snapshot_table_entry_size() +{ + id_len=$(peek_file_be "$1" $(($2 + 12)) 2) + name_len=$(peek_file_be "$1" $(($2 + 14)) 2) + extra_len=$(peek_file_be "$1" $(($2 + 36)) 4) + + full_len=$((40 + extra_len + id_len + name_len)) + echo $(((full_len + 7) / 8 * 8)) +} + +# Parameter: +# $1: image filename +print_snapshot_table() +{ + nb_entries=$(peek_file_be "$1" 60 4) + offset=$(peek_file_be "$1" 64 8) + + echo "Snapshots in $1:" | _filter_testdir | _filter_imgfmt + + for ((i = 0; i < nb_entries; i++)); do + id_len=$(peek_file_be "$1" $((offset + 12)) 2) + name_len=$(peek_file_be "$1" $((offset + 14)) 2) + extra_len=$(peek_file_be "$1" $((offset + 36)) 4) + + extra_ofs=$((offset + 40)) + id_ofs=$((extra_ofs + extra_len)) + name_ofs=$((id_ofs + id_len)) + + echo " [$i]" + echo " ID: $(peek_file_raw "$1" $id_ofs $id_len)" + echo " Name: $(peek_file_raw "$1" $name_ofs $name_len)" + echo " Extra data size: $extra_len" + if [ $extra_len -ge 8 ]; then + echo " VM state size: $(peek_file_be "$1" $extra_ofs 8)" + fi + if [ $extra_len -ge 16 ]; then + echo " Disk size: $(peek_file_be "$1" $((extra_ofs + 8)) 8)" + fi + if [ $extra_len -gt 16 ]; then + echo ' Unknown extra data:' \ + "$(peek_file_raw "$1" $((extra_ofs + 16)) $((extra_len - 16)) \ + | tr -d '\0')" + fi + + offset=$((offset + $(snapshot_table_entry_size "$1" $offset))) + done +} + +# Mark clusters as allocated; works only in refblock 0 (i.e. before +# cluster #32768). +# Parameters: +# $1: Start offset of what to allocate +# $2: End offset (exclusive) +refblock0_allocate() +{ + reftable_ofs=$(peek_file_be "$TEST_IMG" 48 8) + refblock_ofs=$(peek_file_be "$TEST_IMG" $reftable_ofs 8) + + cluster=$(($1 / 65536)) + ecluster=$((($2 + 65535) / 65536)) + + while [ $cluster -lt $ecluster ]; do + if [ $cluster -ge 32768 ]; then + echo "*** Abort: Cluster $cluster exceeds refblock 0 ***" + exit 1 + fi + poke_file "$TEST_IMG" $((refblock_ofs + cluster * 2)) '\x00\x01' + cluster=$((cluster + 1)) + done +} + + +echo +echo '=== Create v2 template ===' +echo + +# Create v2 image with a snapshot table with three entries: +# [0]: No extra data (valid with v2, not valid with v3) +# [1]: Has extra data unknown to qemu +# [2]: Has the 64-bit VM state size, but not the disk size (again, +# valid with v2, not valid with v3) + +TEST_IMG="$TEST_IMG.v2.orig" IMGOPTS='compat=0.10' _make_test_img 64M +$QEMU_IMG snapshot -c sn0 "$TEST_IMG.v2.orig" +$QEMU_IMG snapshot -c sn1 "$TEST_IMG.v2.orig" +$QEMU_IMG snapshot -c sn2 "$TEST_IMG.v2.orig" + +# Copy out all existing snapshot table entries +sn_table_ofs=$(peek_file_be "$TEST_IMG.v2.orig" 64 8) + +# ofs: Snapshot table entry offset +# eds: Extra data size +# ids: Name + ID size +# len: Total entry length +sn0_ofs=$sn_table_ofs +sn0_eds=$(peek_file_be "$TEST_IMG.v2.orig" $((sn0_ofs + 36)) 4) +sn0_ids=$(($(peek_file_be "$TEST_IMG.v2.orig" $((sn0_ofs + 12)) 2) + + $(peek_file_be "$TEST_IMG.v2.orig" $((sn0_ofs + 14)) 2))) +sn0_len=$(snapshot_table_entry_size "$TEST_IMG.v2.orig" $sn0_ofs) +sn1_ofs=$((sn0_ofs + sn0_len)) +sn1_eds=$(peek_file_be "$TEST_IMG.v2.orig" $((sn1_ofs + 36)) 4) +sn1_ids=$(($(peek_file_be "$TEST_IMG.v2.orig" $((sn1_ofs + 12)) 2) + + $(peek_file_be "$TEST_IMG.v2.orig" $((sn1_ofs + 14)) 2))) +sn1_len=$(snapshot_table_entry_size "$TEST_IMG.v2.orig" $sn1_ofs) +sn2_ofs=$((sn1_ofs + sn1_len)) +sn2_eds=$(peek_file_be "$TEST_IMG.v2.orig" $((sn2_ofs + 36)) 4) +sn2_ids=$(($(peek_file_be "$TEST_IMG.v2.orig" $((sn2_ofs + 12)) 2) + + $(peek_file_be "$TEST_IMG.v2.orig" $((sn2_ofs + 14)) 2))) +sn2_len=$(snapshot_table_entry_size "$TEST_IMG.v2.orig" $sn2_ofs) + +# Data before extra data +dd if="$TEST_IMG.v2.orig" of="$TEST_DIR/sn0-pre" bs=1 skip=$sn0_ofs count=40 \ + &> /dev/null +dd if="$TEST_IMG.v2.orig" of="$TEST_DIR/sn1-pre" bs=1 skip=$sn1_ofs count=40 \ + &> /dev/null +dd if="$TEST_IMG.v2.orig" of="$TEST_DIR/sn2-pre" bs=1 skip=$sn2_ofs count=40 \ + &> /dev/null + +# Extra data +dd if="$TEST_IMG.v2.orig" of="$TEST_DIR/sn0-extra" bs=1 \ + skip=$((sn0_ofs + 40)) count=$sn0_eds &> /dev/null +dd if="$TEST_IMG.v2.orig" of="$TEST_DIR/sn1-extra" bs=1 \ + skip=$((sn1_ofs + 40)) count=$sn1_eds &> /dev/null +dd if="$TEST_IMG.v2.orig" of="$TEST_DIR/sn2-extra" bs=1 \ + skip=$((sn2_ofs + 40)) count=$sn2_eds &> /dev/null + +# Data after extra data +dd if="$TEST_IMG.v2.orig" of="$TEST_DIR/sn0-post" bs=1 \ + skip=$((sn0_ofs + 40 + sn0_eds)) count=$sn0_ids \ + &> /dev/null +dd if="$TEST_IMG.v2.orig" of="$TEST_DIR/sn1-post" bs=1 \ + skip=$((sn1_ofs + 40 + sn1_eds)) count=$sn1_ids \ + &> /dev/null +dd if="$TEST_IMG.v2.orig" of="$TEST_DIR/sn2-post" bs=1 \ + skip=$((sn2_ofs + 40 + sn2_eds)) count=$sn2_ids \ + &> /dev/null + +# Amend them, one by one +# Set sn0's extra data size to 0 +poke_file "$TEST_DIR/sn0-pre" 36 '\x00\x00\x00\x00' +truncate -s 0 "$TEST_DIR/sn0-extra" +# Grow sn0-post to pad +truncate -s $(($(snapshot_table_entry_size "$TEST_DIR/sn0-pre") - 40)) \ + "$TEST_DIR/sn0-post" + +# Set sn1's extra data size to 42 +poke_file "$TEST_DIR/sn1-pre" 36 '\x00\x00\x00\x2a' +truncate -s 42 "$TEST_DIR/sn1-extra" +poke_file "$TEST_DIR/sn1-extra" 16 'very important data' +# Grow sn1-post to pad +truncate -s $(($(snapshot_table_entry_size "$TEST_DIR/sn1-pre") - 82)) \ + "$TEST_DIR/sn1-post" + +# Set sn2's extra data size to 8 +poke_file "$TEST_DIR/sn2-pre" 36 '\x00\x00\x00\x08' +truncate -s 8 "$TEST_DIR/sn2-extra" +# Grow sn2-post to pad +truncate -s $(($(snapshot_table_entry_size "$TEST_DIR/sn2-pre") - 48)) \ + "$TEST_DIR/sn2-post" + +# Construct snapshot table +cat "$TEST_DIR"/sn0-{pre,extra,post} \ + "$TEST_DIR"/sn1-{pre,extra,post} \ + "$TEST_DIR"/sn2-{pre,extra,post} \ + | dd of="$TEST_IMG.v2.orig" bs=1 seek=$sn_table_ofs conv=notrunc \ + &> /dev/null + +# Done! +TEST_IMG="$TEST_IMG.v2.orig" _check_test_img +print_snapshot_table "$TEST_IMG.v2.orig" + +echo +echo '=== Upgrade to v3 ===' +echo + +cp "$TEST_IMG.v2.orig" "$TEST_IMG.v3.orig" +$QEMU_IMG amend -o compat=1.1 "$TEST_IMG.v3.orig" +TEST_IMG="$TEST_IMG.v3.orig" _check_test_img +print_snapshot_table "$TEST_IMG.v3.orig" + +echo +echo '=== Repair botched v3 ===' +echo + +# Force the v2 file to be v3. v3 requires each snapshot table entry +# to have at least 16 bytes of extra data, so it will not comply to +# the qcow2 v3 specification; but we can fix that. +cp "$TEST_IMG.v2.orig" "$TEST_IMG" + +# Set version +poke_file "$TEST_IMG" 4 '\x00\x00\x00\x03' +# Increase header length (necessary for v3) +poke_file "$TEST_IMG" 100 '\x00\x00\x00\x68' +# Set refcount order (necessary for v3) +poke_file "$TEST_IMG" 96 '\x00\x00\x00\x04' + +_check_test_img -r all +print_snapshot_table "$TEST_IMG" + + +# From now on, just test the qcow2 version we are supposed to test. +# (v3 by default, v2 by choice through $IMGOPTS.) +# That works because we always write all known extra data when +# updating the snapshot table, independent of the version. + +if echo "$IMGOPTS" | grep -q 'compat=\(0\.10\|v2\)' 2> /dev/null; then + subver=v2 +else + subver=v3 +fi + +echo +echo '=== Add new snapshot ===' +echo + +cp "$TEST_IMG.$subver.orig" "$TEST_IMG" +$QEMU_IMG snapshot -c sn3 "$TEST_IMG" +_check_test_img +print_snapshot_table "$TEST_IMG" + +echo +echo '=== Remove different snapshots ===' + +for sn in sn0 sn1 sn2; do + echo + echo "--- $sn ---" + + cp "$TEST_IMG.$subver.orig" "$TEST_IMG" + $QEMU_IMG snapshot -d $sn "$TEST_IMG" + _check_test_img + print_snapshot_table "$TEST_IMG" +done + +echo +echo '=== Reject too much unknown extra data ===' +echo + +cp "$TEST_IMG.$subver.orig" "$TEST_IMG" +$QEMU_IMG snapshot -c sn3 "$TEST_IMG" + +sn_table_ofs=$(peek_file_be "$TEST_IMG" 64 8) +sn0_ofs=$sn_table_ofs +sn1_ofs=$((sn0_ofs + $(snapshot_table_entry_size "$TEST_IMG" $sn0_ofs))) +sn2_ofs=$((sn1_ofs + $(snapshot_table_entry_size "$TEST_IMG" $sn1_ofs))) +sn3_ofs=$((sn2_ofs + $(snapshot_table_entry_size "$TEST_IMG" $sn2_ofs))) + +# 64 kB of extra data should be rejected +# (Note that this also induces a refcount error, because it spills +# over to the next cluster. That's a good way to test that we can +# handle simultaneous snapshot table and refcount errors.) +poke_file "$TEST_IMG" $((sn3_ofs + 36)) '\x00\x01\x00\x00' + +# Print error +_img_info +echo +_check_test_img +echo + +# Should be repairable +_check_test_img -r all + +echo +echo '=== Snapshot table too big ===' +echo + +sn_table_ofs=$(peek_file_be "$TEST_IMG.v3.orig" 64 8) + +# Fill a snapshot with 1 kB of extra data, a 65535-char ID, and a +# 65535-char name, and repeat it as many times as necessary to fill +# 64 MB (the maximum supported by qemu) + +touch "$TEST_DIR/sn0" + +# Full size (fixed + extra + ID + name + padding) +sn_size=$((40 + 1024 + 65535 + 65535 + 2)) + +# We only need the fixed part, though. +truncate -s 40 "$TEST_DIR/sn0" + +# 65535-char ID string +poke_file "$TEST_DIR/sn0" 12 '\xff\xff' +# 65535-char name +poke_file "$TEST_DIR/sn0" 14 '\xff\xff' +# 1 kB of extra data +poke_file "$TEST_DIR/sn0" 36 '\x00\x00\x04\x00' + +# Create test image +_make_test_img 64M + +# Hook up snapshot table somewhere safe (at 1 MB) +poke_file "$TEST_IMG" 64 '\x00\x00\x00\x00\x00\x10\x00\x00' + +offset=1048576 +size_written=0 +sn_count=0 +while [ $size_written -le $((64 * 1048576)) ]; do + dd if="$TEST_DIR/sn0" of="$TEST_IMG" bs=1 seek=$offset conv=notrunc \ + &> /dev/null + offset=$((offset + sn_size)) + size_written=$((size_written + sn_size)) + sn_count=$((sn_count + 1)) +done +truncate -s "$offset" "$TEST_IMG" + +# Give the last snapshot (the one to be removed) an L1 table so we can +# see how that is handled when repairing the image +# (Put it two clusters before 1 MB, and one L2 table one cluster +# before 1 MB) +poke_file "$TEST_IMG" $((offset - sn_size + 0)) \ + '\x00\x00\x00\x00\x00\x0e\x00\x00' +poke_file "$TEST_IMG" $((offset - sn_size + 8)) \ + '\x00\x00\x00\x01' + +# Hook up the L2 table +poke_file "$TEST_IMG" $((1048576 - 2 * 65536)) \ + '\x80\x00\x00\x00\x00\x0f\x00\x00' + +# Make sure all of the clusters we just hooked up are allocated: +# - The snapshot table +# - The last snapshot's L1 and L2 table +refblock0_allocate $((1048576 - 2 * 65536)) $offset + +poke_file "$TEST_IMG" 60 \ + "$(printf '%08x' $sn_count | sed -e 's/\(..\)/\\x\1/g')" + +# Print error +_img_info +echo +_check_test_img +echo + +# Should be repairable +_check_test_img -r all + +echo +echo "$((sn_count - 1)) snapshots should remain:" +echo " qemu-img info reports $(_img_info | grep -c '^ \{34\}') snapshots" +echo " Image header reports $(peek_file_be "$TEST_IMG" 60 4) snapshots" + +echo +echo '=== Snapshot table too big with one entry with too much extra data ===' +echo + +# For this test, we reuse the image from the previous case, which has +# a snapshot table that is right at the limit. +# Our layout looks like this: +# - (a number of snapshot table entries) +# - One snapshot with $extra_data_size extra data +# - One normal snapshot that breaks the 64 MB boundary +# - One normal snapshot beyond the 64 MB boundary +# +# $extra_data_size is calculated so that simply by virtue of it +# decreasing to 1 kB, the penultimate snapshot will fit into 64 MB +# limit again. The final snapshot will always be beyond the limit, so +# that we can see that the repair algorithm does still determine the +# limit to be somewhere, even when truncating one snapshot's extra +# data. + +# The last case has removed the last snapshot, so calculate +# $old_offset to get the current image's real length +old_offset=$((offset - sn_size)) + +# The layout from the previous test had one snapshot beyond the 64 MB +# limit; we want the same (after the oversized extra data has been +# truncated to 1 kB), so we drop the last three snapshots and +# construct them from scratch. +offset=$((offset - 3 * sn_size)) +sn_count=$((sn_count - 3)) + +# Assuming we had already written one of the three snapshots +# (necessary so we can calculate $extra_data_size next). +size_written=$((size_written - 2 * sn_size)) + +# Increase the extra data size so we go past the limit +# (The -1024 comes from the 1 kB of extra data we already have) +extra_data_size=$((64 * 1048576 + 8 - sn_size - (size_written - 1024))) + +poke_file "$TEST_IMG" $((offset + 36)) \ + "$(printf '%08x' $extra_data_size | sed -e 's/\(..\)/\\x\1/g')" + +offset=$((offset + sn_size - 1024 + extra_data_size)) +size_written=$((size_written - 1024 + extra_data_size)) +sn_count=$((sn_count + 1)) + +# Write the two normal snapshots +for ((i = 0; i < 2; i++)); do + dd if="$TEST_DIR/sn0" of="$TEST_IMG" bs=1 seek=$offset conv=notrunc \ + &> /dev/null + offset=$((offset + sn_size)) + size_written=$((size_written + sn_size)) + sn_count=$((sn_count + 1)) + + if [ $i = 0 ]; then + # Check that the penultimate snapshot is beyond the 64 MB limit + echo "Snapshot table size should equal $((64 * 1048576 + 8)):" \ + $size_written + echo + fi +done + +truncate -s $offset "$TEST_IMG" +refblock0_allocate $old_offset $offset + +poke_file "$TEST_IMG" 60 \ + "$(printf '%08x' $sn_count | sed -e 's/\(..\)/\\x\1/g')" + +# Print error +_img_info +echo +_check_test_img +echo + +# Just truncating the extra data should be sufficient to shorten the +# snapshot table so only one snapshot exceeds the extra size +_check_test_img -r all + +echo +echo '=== Too many snapshots ===' +echo + +# Create a v2 image, for speeds' sake: All-zero snapshot table entries +# are only valid in v2. +IMGOPTS='compat=0.10' _make_test_img 64M + +# Hook up snapshot table somewhere safe (at 1 MB) +poke_file "$TEST_IMG" 64 '\x00\x00\x00\x00\x00\x10\x00\x00' +# "Create" more than 65536 snapshots (twice that many here) +poke_file "$TEST_IMG" 60 '\x00\x02\x00\x00' + +# 40-byte all-zero snapshot table entries are valid snapshots, but +# only in v2 (v3 needs 16 bytes of extra data, so we would have to +# write 131072x '\x10'). +truncate -s $((1048576 + 40 * 131072)) "$TEST_IMG" + +# But let us give one of the snapshots to be removed an L1 table so +# we can see how that is handled when repairing the image. +# (Put it two clusters before 1 MB, and one L2 table one cluster +# before 1 MB) +poke_file "$TEST_IMG" $((1048576 + 40 * 65536 + 0)) \ + '\x00\x00\x00\x00\x00\x0e\x00\x00' +poke_file "$TEST_IMG" $((1048576 + 40 * 65536 + 8)) \ + '\x00\x00\x00\x01' + +# Hook up the L2 table +poke_file "$TEST_IMG" $((1048576 - 2 * 65536)) \ + '\x80\x00\x00\x00\x00\x0f\x00\x00' + +# Make sure all of the clusters we just hooked up are allocated: +# - The snapshot table +# - The last snapshot's L1 and L2 table +refblock0_allocate $((1048576 - 2 * 65536)) $((1048576 + 40 * 131072)) + +# Print error +_img_info +echo +_check_test_img +echo + +# Should be repairable +_check_test_img -r all + +echo +echo '65536 snapshots should remain:' +echo " qemu-img info reports $(_img_info | grep -c '^ \{34\}') snapshots" +echo " Image header reports $(peek_file_be "$TEST_IMG" 60 4) snapshots" + +# success, all done +echo "*** done" +status=0 diff --git a/tests/qemu-iotests/261.out b/tests/qemu-iotests/261.out new file mode 100644 index 0000000000..2600354566 --- /dev/null +++ b/tests/qemu-iotests/261.out @@ -0,0 +1,346 @@ +QA output created by 261 + +=== Create v2 template === + +Formatting 'TEST_DIR/t.IMGFMT.v2.orig', fmt=IMGFMT size=67108864 +No errors were found on the image. +Snapshots in TEST_DIR/t.IMGFMT.v2.orig: + [0] + ID: 1 + Name: sn0 + Extra data size: 0 + [1] + ID: 2 + Name: sn1 + Extra data size: 42 + VM state size: 0 + Disk size: 67108864 + Unknown extra data: very important data + [2] + ID: 3 + Name: sn2 + Extra data size: 8 + VM state size: 0 + +=== Upgrade to v3 === + +No errors were found on the image. +Snapshots in TEST_DIR/t.IMGFMT.v3.orig: + [0] + ID: 1 + Name: sn0 + Extra data size: 16 + VM state size: 0 + Disk size: 67108864 + [1] + ID: 2 + Name: sn1 + Extra data size: 42 + VM state size: 0 + Disk size: 67108864 + Unknown extra data: very important data + [2] + ID: 3 + Name: sn2 + Extra data size: 16 + VM state size: 0 + Disk size: 67108864 + +=== Repair botched v3 === + +Repairing snapshot table entry 0 is incomplete +Repairing snapshot table entry 2 is incomplete +The following inconsistencies were found and repaired: + + 0 leaked clusters + 2 corruptions + +Double checking the fixed image now... +No errors were found on the image. +Snapshots in TEST_DIR/t.IMGFMT: + [0] + ID: 1 + Name: sn0 + Extra data size: 16 + VM state size: 0 + Disk size: 67108864 + [1] + ID: 2 + Name: sn1 + Extra data size: 42 + VM state size: 0 + Disk size: 67108864 + Unknown extra data: very important data + [2] + ID: 3 + Name: sn2 + Extra data size: 16 + VM state size: 0 + Disk size: 67108864 + +=== Add new snapshot === + +No errors were found on the image. +Snapshots in TEST_DIR/t.IMGFMT: + [0] + ID: 1 + Name: sn0 + Extra data size: 16 + VM state size: 0 + Disk size: 67108864 + [1] + ID: 2 + Name: sn1 + Extra data size: 42 + VM state size: 0 + Disk size: 67108864 + Unknown extra data: very important data + [2] + ID: 3 + Name: sn2 + Extra data size: 16 + VM state size: 0 + Disk size: 67108864 + [3] + ID: 4 + Name: sn3 + Extra data size: 16 + VM state size: 0 + Disk size: 67108864 + +=== Remove different snapshots === + +--- sn0 --- +No errors were found on the image. +Snapshots in TEST_DIR/t.IMGFMT: + [0] + ID: 2 + Name: sn1 + Extra data size: 42 + VM state size: 0 + Disk size: 67108864 + Unknown extra data: very important data + [1] + ID: 3 + Name: sn2 + Extra data size: 16 + VM state size: 0 + Disk size: 67108864 + +--- sn1 --- +No errors were found on the image. +Snapshots in TEST_DIR/t.IMGFMT: + [0] + ID: 1 + Name: sn0 + Extra data size: 16 + VM state size: 0 + Disk size: 67108864 + [1] + ID: 3 + Name: sn2 + Extra data size: 16 + VM state size: 0 + Disk size: 67108864 + +--- sn2 --- +No errors were found on the image. +Snapshots in TEST_DIR/t.IMGFMT: + [0] + ID: 1 + Name: sn0 + Extra data size: 16 + VM state size: 0 + Disk size: 67108864 + [1] + ID: 2 + Name: sn1 + Extra data size: 42 + VM state size: 0 + Disk size: 67108864 + Unknown extra data: very important data + +=== Reject too much unknown extra data === + +qemu-img: Could not open 'TEST_DIR/t.IMGFMT': Too much extra metadata in snapshot table entry 3 +You can force-remove this extra metadata with qemu-img check -r all + +qemu-img: ERROR failed to read the snapshot table: Too much extra metadata in snapshot table entry 3 +You can force-remove this extra metadata with qemu-img check -r all +qemu-img: Check failed: File too large + +Discarding too much extra metadata in snapshot table entry 3 (65536 > 1024) +ERROR cluster 10 refcount=0 reference=1 +Rebuilding refcount structure +Repairing cluster 1 refcount=1 reference=0 +Repairing cluster 2 refcount=1 reference=0 +The following inconsistencies were found and repaired: + + 0 leaked clusters + 2 corruptions + +Double checking the fixed image now... +No errors were found on the image. + +=== Snapshot table too big === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +qemu-img: Could not open 'TEST_DIR/t.IMGFMT': Snapshot table is too big +You can force-remove all 1 overhanging snapshots with qemu-img check -r all + +qemu-img: ERROR failed to read the snapshot table: Snapshot table is too big +You can force-remove all 1 overhanging snapshots with qemu-img check -r all +qemu-img: Check failed: File too large + +Discarding 1 overhanging snapshots (snapshot table is too big) +Leaked cluster 14 refcount=1 reference=0 +Leaked cluster 15 refcount=1 reference=0 +Leaked cluster 1039 refcount=1 reference=0 +Leaked cluster 1040 refcount=1 reference=0 +Repairing cluster 14 refcount=1 reference=0 +Repairing cluster 15 refcount=1 reference=0 +Repairing cluster 1039 refcount=1 reference=0 +Repairing cluster 1040 refcount=1 reference=0 +The following inconsistencies were found and repaired: + + 4 leaked clusters + 1 corruptions + +Double checking the fixed image now... +No errors were found on the image. + +507 snapshots should remain: + qemu-img info reports 507 snapshots + Image header reports 507 snapshots + +=== Snapshot table too big with one entry with too much extra data === + +Snapshot table size should equal 67108872: 67108872 + +qemu-img: Could not open 'TEST_DIR/t.IMGFMT': Too much extra metadata in snapshot table entry 505 +You can force-remove this extra metadata with qemu-img check -r all + +qemu-img: ERROR failed to read the snapshot table: Too much extra metadata in snapshot table entry 505 +You can force-remove this extra metadata with qemu-img check -r all +qemu-img: Check failed: File too large + +Discarding too much extra metadata in snapshot table entry 505 (116944 > 1024) +Discarding 1 overhanging snapshots (snapshot table is too big) +Leaked cluster 1041 refcount=1 reference=0 +Leaked cluster 1042 refcount=1 reference=0 +Repairing cluster 1041 refcount=1 reference=0 +Repairing cluster 1042 refcount=1 reference=0 +The following inconsistencies were found and repaired: + + 2 leaked clusters + 2 corruptions + +Double checking the fixed image now... +No errors were found on the image. + +=== Too many snapshots === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +qemu-img: Could not open 'TEST_DIR/t.IMGFMT': Snapshot table too large + +qemu-img: ERROR snapshot table too large +You can force-remove all 65536 overhanging snapshots with qemu-img check -r all +qemu-img: Check failed: File too large + +Discarding 65536 overhanging snapshots +Leaked cluster 14 refcount=1 reference=0 +Leaked cluster 15 refcount=1 reference=0 +Leaked cluster 56 refcount=1 reference=0 +Leaked cluster 57 refcount=1 reference=0 +Leaked cluster 58 refcount=1 reference=0 +Leaked cluster 59 refcount=1 reference=0 +Leaked cluster 60 refcount=1 reference=0 +Leaked cluster 61 refcount=1 reference=0 +Leaked cluster 62 refcount=1 reference=0 +Leaked cluster 63 refcount=1 reference=0 +Leaked cluster 64 refcount=1 reference=0 +Leaked cluster 65 refcount=1 reference=0 +Leaked cluster 66 refcount=1 reference=0 +Leaked cluster 67 refcount=1 reference=0 +Leaked cluster 68 refcount=1 reference=0 +Leaked cluster 69 refcount=1 reference=0 +Leaked cluster 70 refcount=1 reference=0 +Leaked cluster 71 refcount=1 reference=0 +Leaked cluster 72 refcount=1 reference=0 +Leaked cluster 73 refcount=1 reference=0 +Leaked cluster 74 refcount=1 reference=0 +Leaked cluster 75 refcount=1 reference=0 +Leaked cluster 76 refcount=1 reference=0 +Leaked cluster 77 refcount=1 reference=0 +Leaked cluster 78 refcount=1 reference=0 +Leaked cluster 79 refcount=1 reference=0 +Leaked cluster 80 refcount=1 reference=0 +Leaked cluster 81 refcount=1 reference=0 +Leaked cluster 82 refcount=1 reference=0 +Leaked cluster 83 refcount=1 reference=0 +Leaked cluster 84 refcount=1 reference=0 +Leaked cluster 85 refcount=1 reference=0 +Leaked cluster 86 refcount=1 reference=0 +Leaked cluster 87 refcount=1 reference=0 +Leaked cluster 88 refcount=1 reference=0 +Leaked cluster 89 refcount=1 reference=0 +Leaked cluster 90 refcount=1 reference=0 +Leaked cluster 91 refcount=1 reference=0 +Leaked cluster 92 refcount=1 reference=0 +Leaked cluster 93 refcount=1 reference=0 +Leaked cluster 94 refcount=1 reference=0 +Leaked cluster 95 refcount=1 reference=0 +Repairing cluster 14 refcount=1 reference=0 +Repairing cluster 15 refcount=1 reference=0 +Repairing cluster 56 refcount=1 reference=0 +Repairing cluster 57 refcount=1 reference=0 +Repairing cluster 58 refcount=1 reference=0 +Repairing cluster 59 refcount=1 reference=0 +Repairing cluster 60 refcount=1 reference=0 +Repairing cluster 61 refcount=1 reference=0 +Repairing cluster 62 refcount=1 reference=0 +Repairing cluster 63 refcount=1 reference=0 +Repairing cluster 64 refcount=1 reference=0 +Repairing cluster 65 refcount=1 reference=0 +Repairing cluster 66 refcount=1 reference=0 +Repairing cluster 67 refcount=1 reference=0 +Repairing cluster 68 refcount=1 reference=0 +Repairing cluster 69 refcount=1 reference=0 +Repairing cluster 70 refcount=1 reference=0 +Repairing cluster 71 refcount=1 reference=0 +Repairing cluster 72 refcount=1 reference=0 +Repairing cluster 73 refcount=1 reference=0 +Repairing cluster 74 refcount=1 reference=0 +Repairing cluster 75 refcount=1 reference=0 +Repairing cluster 76 refcount=1 reference=0 +Repairing cluster 77 refcount=1 reference=0 +Repairing cluster 78 refcount=1 reference=0 +Repairing cluster 79 refcount=1 reference=0 +Repairing cluster 80 refcount=1 reference=0 +Repairing cluster 81 refcount=1 reference=0 +Repairing cluster 82 refcount=1 reference=0 +Repairing cluster 83 refcount=1 reference=0 +Repairing cluster 84 refcount=1 reference=0 +Repairing cluster 85 refcount=1 reference=0 +Repairing cluster 86 refcount=1 reference=0 +Repairing cluster 87 refcount=1 reference=0 +Repairing cluster 88 refcount=1 reference=0 +Repairing cluster 89 refcount=1 reference=0 +Repairing cluster 90 refcount=1 reference=0 +Repairing cluster 91 refcount=1 reference=0 +Repairing cluster 92 refcount=1 reference=0 +Repairing cluster 93 refcount=1 reference=0 +Repairing cluster 94 refcount=1 reference=0 +Repairing cluster 95 refcount=1 reference=0 +The following inconsistencies were found and repaired: + + 42 leaked clusters + 65536 corruptions + +Double checking the fixed image now... +No errors were found on the image. + +65536 snapshots should remain: + qemu-img info reports 65536 snapshots + Image header reports 65536 snapshots +*** done diff --git a/tests/qemu-iotests/264 b/tests/qemu-iotests/264 index c8cd97ae2b..131366422b 100755 --- a/tests/qemu-iotests/264 +++ b/tests/qemu-iotests/264 @@ -24,6 +24,8 @@ import iotests from iotests import qemu_img_create, qemu_io_silent_check, file_path, \ qemu_nbd_popen, log +iotests.verify_image_format(supported_fmts=['qcow2']) + disk_a, disk_b, nbd_sock = file_path('disk_a', 'disk_b', 'nbd-sock') nbd_uri = 'nbd+unix:///?socket=' + nbd_sock size = 5 * 1024 * 1024 diff --git a/tests/qemu-iotests/267 b/tests/qemu-iotests/267 index d37a67c012..170e173c0a 100755 --- a/tests/qemu-iotests/267 +++ b/tests/qemu-iotests/267 @@ -29,7 +29,7 @@ status=1 # failure is the default! _cleanup() { _cleanup_test_img - rm -f "$TEST_DIR/nbd" + rm -f "$SOCK_DIR/nbd" } trap "_cleanup; exit \$status" 0 1 2 3 15 @@ -143,7 +143,7 @@ echo IMGOPTS="backing_file=$TEST_IMG.base" _make_test_img $size cat <<EOF | -nbd_server_start unix:$TEST_DIR/nbd +nbd_server_start unix:$SOCK_DIR/nbd nbd_server_add -w backing-fmt savevm snap0 info snapshots diff --git a/tests/qemu-iotests/267.out b/tests/qemu-iotests/267.out index 9d812e3c72..8dddb4baa4 100644 --- a/tests/qemu-iotests/267.out +++ b/tests/qemu-iotests/267.out @@ -161,7 +161,7 @@ Internal snapshots on backing file: Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT.base,node-name=backing-file -blockdev driver=IMGFMT,file=backing-file,node-name=backing-fmt -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=file -blockdev driver=IMGFMT,file=file,backing=backing-fmt,node-name=fmt QEMU X.Y.Z monitor - type 'help' for more information -(qemu) nbd_server_start unix:TEST_DIR/nbd +(qemu) nbd_server_start unix:SOCK_DIR/nbd (qemu) nbd_server_add -w backing-fmt (qemu) savevm snap0 (qemu) info snapshots diff --git a/tests/qemu-iotests/check b/tests/qemu-iotests/check index 588c453a94..71fe38834e 100755 --- a/tests/qemu-iotests/check +++ b/tests/qemu-iotests/check @@ -97,6 +97,7 @@ IMGFMT -- $FULL_IMGFMT_DETAILS IMGPROTO -- $IMGPROTO PLATFORM -- $FULL_HOST_DETAILS TEST_DIR -- $TEST_DIR +SOCK_DIR -- $SOCK_DIR SOCKET_SCM_HELPER -- $SOCKET_SCM_HELPER EOF @@ -116,10 +117,14 @@ set_prog_path() if [ -z "$TEST_DIR" ]; then TEST_DIR=$PWD/scratch fi +mkdir -p "$TEST_DIR" || _init_error 'Failed to create TEST_DIR' -if [ ! -e "$TEST_DIR" ]; then - mkdir "$TEST_DIR" +tmp_sock_dir=false +if [ -z "$SOCK_DIR" ]; then + SOCK_DIR=$(mktemp -d) + tmp_sock_dir=true fi +mkdir -p "$SOCK_DIR" || _init_error 'Failed to create SOCK_DIR' diff="diff -u" verbose=false @@ -534,6 +539,7 @@ if [ -z "$SAMPLE_IMG_DIR" ]; then fi export TEST_DIR +export SOCK_DIR export SAMPLE_IMG_DIR if [ -s $tmp.list ] @@ -716,6 +722,11 @@ END { if (NR > 0) { rm -f "${TEST_DIR}"/*.out "${TEST_DIR}"/*.err "${TEST_DIR}"/*.time rm -f "${TEST_DIR}"/check.pid "${TEST_DIR}"/check.sts rm -f $tmp.* + + if $tmp_sock_dir + then + rm -rf "$SOCK_DIR" + fi } trap "_wrapup; exit \$status" 0 1 2 3 15 diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter index 9f418b4881..f870e00e44 100644 --- a/tests/qemu-iotests/common.filter +++ b/tests/qemu-iotests/common.filter @@ -43,7 +43,8 @@ _filter_qom_path() # replace occurrences of the actual TEST_DIR value with TEST_DIR _filter_testdir() { - $SED -e "s#$TEST_DIR/#TEST_DIR/#g" + $SED -e "s#$TEST_DIR/#TEST_DIR/#g" \ + -e "s#$SOCK_DIR/#SOCK_DIR/#g" } # replace occurrences of the actual IMGFMT value with IMGFMT @@ -124,6 +125,7 @@ _filter_img_create() $SED -e "s#$REMOTE_TEST_DIR#TEST_DIR#g" \ -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \ -e "s#$TEST_DIR#TEST_DIR#g" \ + -e "s#$SOCK_DIR#SOCK_DIR#g" \ -e "s#$IMGFMT#IMGFMT#g" \ -e 's#nbd:127.0.0.1:10810#TEST_DIR/t.IMGFMT#g' \ -e "s# encryption=off##g" \ @@ -160,6 +162,7 @@ _filter_img_info() $SED -e "s#$REMOTE_TEST_DIR#TEST_DIR#g" \ -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \ -e "s#$TEST_DIR#TEST_DIR#g" \ + -e "s#$SOCK_DIR#SOCK_DIR#g" \ -e "s#$IMGFMT#IMGFMT#g" \ -e 's#nbd://127.0.0.1:10810$#TEST_DIR/t.IMGFMT#g' \ -e 's#json.*vdisk-id.*vxhs"}}#TEST_DIR/t.IMGFMT#' \ @@ -218,7 +221,7 @@ _filter_nbd() # Filter out the TCP port number since this changes between runs. $SED -e '/nbd\/.*\.c:/d' \ -e 's#127\.0\.0\.1:[0-9]*#127.0.0.1:PORT#g' \ - -e "s#?socket=$TEST_DIR#?socket=TEST_DIR#g" \ + -e "s#?socket=$SOCK_DIR#?socket=SOCK_DIR#g" \ -e 's#\(foo\|PORT/\?\|.sock\): Failed to .*$#\1#' } diff --git a/tests/qemu-iotests/common.nbd b/tests/qemu-iotests/common.nbd index 24b01b60aa..a8cae8fe2c 100644 --- a/tests/qemu-iotests/common.nbd +++ b/tests/qemu-iotests/common.nbd @@ -19,7 +19,7 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # -nbd_unix_socket="${TEST_DIR}/qemu-nbd.sock" +nbd_unix_socket="${SOCK_DIR}/qemu-nbd.sock" nbd_tcp_addr="127.0.0.1" nbd_pid_file="${TEST_DIR}/qemu-nbd.pid" nbd_stderr_fifo="${TEST_DIR}/qemu-nbd.fifo" diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc index 12b4751848..fa7bae2422 100644 --- a/tests/qemu-iotests/common.rc +++ b/tests/qemu-iotests/common.rc @@ -53,6 +53,26 @@ poke_file() printf "$3" | dd "of=$1" bs=1 "seek=$2" conv=notrunc &>/dev/null } +# peek_file_le 'test.img' 512 2 => 65534 +peek_file_le() +{ + # Wrap in echo $() to strip spaces + echo $(od -j"$2" -N"$3" --endian=little -An -vtu"$3" "$1") +} + +# peek_file_be 'test.img' 512 2 => 65279 +peek_file_be() +{ + # Wrap in echo $() to strip spaces + echo $(od -j"$2" -N"$3" --endian=big -An -vtu"$3" "$1") +} + +# peek_file_raw 'test.img' 512 2 => '\xff\xfe' +peek_file_raw() +{ + dd if="$1" bs=1 skip="$2" count="$3" status=none +} + if ! . ./common.config then diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index af322af756..28871604cd 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -274,6 +274,7 @@ 257 rw 258 rw quick 260 rw quick +261 rw 262 rw quick migration 263 rw quick 264 rw diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index 693fde155a..075f4739da 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -57,6 +57,7 @@ qemu_opts = os.environ.get('QEMU_OPTIONS', '').strip().split(' ') imgfmt = os.environ.get('IMGFMT', 'raw') imgproto = os.environ.get('IMGPROTO', 'file') test_dir = os.environ.get('TEST_DIR') +sock_dir = os.environ.get('SOCK_DIR') output_dir = os.environ.get('OUTPUT_DIR', '.') cachemode = os.environ.get('CACHEMODE') qemu_default_machine = os.environ.get('QEMU_DEFAULT_MACHINE') @@ -385,10 +386,10 @@ class FilePaths(object): qemu_img('create', img_path, '1G') # migration_sock_path is automatically deleted """ - def __init__(self, names): + def __init__(self, names, base_dir=test_dir): self.paths = [] for name in names: - self.paths.append(os.path.join(test_dir, file_pattern(name))) + self.paths.append(os.path.join(base_dir, file_pattern(name))) def __enter__(self): return self.paths @@ -405,8 +406,8 @@ class FilePath(FilePaths): """ FilePath is a specialization of FilePaths that takes a single filename. """ - def __init__(self, name): - super(FilePath, self).__init__([name]) + def __init__(self, name, base_dir=test_dir): + super(FilePath, self).__init__([name], base_dir) def __enter__(self): return self.paths[0] @@ -419,7 +420,7 @@ def file_path_remover(): pass -def file_path(*names): +def file_path(*names, base_dir=test_dir): ''' Another way to get auto-generated filename that cleans itself up. Use is as simple as: @@ -435,7 +436,7 @@ def file_path(*names): paths = [] for name in names: filename = file_pattern(name) - path = os.path.join(test_dir, filename) + path = os.path.join(base_dir, filename) file_path_remover.paths.append(path) paths.append(path) @@ -456,7 +457,8 @@ class VM(qtest.QEMUQtestMachine): name = "qemu%s-%d" % (path_suffix, os.getpid()) super(VM, self).__init__(qemu_prog, qemu_opts, name=name, test_dir=test_dir, - socket_scm_helper=socket_scm_helper) + socket_scm_helper=socket_scm_helper, + sock_dir=sock_dir) self._num_drives = 0 def add_object(self, opts): @@ -838,6 +840,11 @@ class QMPTestCase(unittest.TestCase): return self.pause_wait(job_id) return result + def case_skip(self, reason): + '''Skip this test case''' + case_notrun(reason) + self.skipTest(reason) + def notrun(reason): '''Skip this test suite''' @@ -849,7 +856,11 @@ def notrun(reason): sys.exit(0) def case_notrun(reason): - '''Skip this test case''' + '''Mark this test case as not having been run (without actually + skipping it, that is left to the caller). See + QMPTestCase.case_skip() for a variant that actually skips the + current test case.''' + # Each test in qemu-iotests has a number ("seq") seq = os.path.basename(sys.argv[0]) @@ -912,25 +923,47 @@ def qemu_pipe(*args): def supported_formats(read_only=False): '''Set 'read_only' to True to check ro-whitelist Otherwise, rw-whitelist is checked''' - format_message = qemu_pipe("-drive", "format=help") - line = 1 if read_only else 0 - return format_message.splitlines()[line].split(":")[1].split() + + if not hasattr(supported_formats, "formats"): + supported_formats.formats = {} + + if read_only not in supported_formats.formats: + format_message = qemu_pipe("-drive", "format=help") + line = 1 if read_only else 0 + supported_formats.formats[read_only] = \ + format_message.splitlines()[line].split(":")[1].split() + + return supported_formats.formats[read_only] def skip_if_unsupported(required_formats=[], read_only=False): '''Skip Test Decorator Runs the test if all the required formats are whitelisted''' def skip_test_decorator(func): - def func_wrapper(*args, **kwargs): - usf_list = list(set(required_formats) - - set(supported_formats(read_only))) + def func_wrapper(test_case: QMPTestCase, *args, **kwargs): + if callable(required_formats): + fmts = required_formats(test_case) + else: + fmts = required_formats + + usf_list = list(set(fmts) - set(supported_formats(read_only))) if usf_list: - case_notrun('{}: formats {} are not whitelisted'.format( - args[0], usf_list)) + test_case.case_skip('{}: formats {} are not whitelisted'.format( + test_case, usf_list)) else: - return func(*args, **kwargs) + return func(test_case, *args, **kwargs) return func_wrapper return skip_test_decorator +def skip_if_user_is_root(func): + '''Skip Test Decorator + Runs the test only without root permissions''' + def func_wrapper(*args, **kwargs): + if os.getuid() == 0: + case_notrun('{}: cannot be run as root'.format(args[0])) + else: + return func(*args, **kwargs) + return func_wrapper + def execute_unittest(output, verbosity, debug): runner = unittest.TextTestRunner(stream=output, descriptions=True, verbosity=verbosity) @@ -940,8 +973,15 @@ def execute_unittest(output, verbosity, debug): unittest.main(testRunner=runner) finally: if not debug: - sys.stderr.write(re.sub(r'Ran (\d+) tests? in [\d.]+s', - r'Ran \1 tests', output.getvalue())) + out = output.getvalue() + out = re.sub(r'Ran (\d+) tests? in [\d.]+s', r'Ran \1 tests', out) + + # Hide skipped tests from the reference output + out = re.sub(r'OK \(skipped=\d+\)', 'OK', out) + out_first_line, out_rest = out.split('\n', 1) + out = out_first_line.replace('s', '.') + '\n' + out_rest + + sys.stderr.write(out) def execute_test(test_function=None, supported_fmts=[], supported_oses=['linux'], diff --git a/tests/rtc-test.c b/tests/rtc-test.c index 6309b0ef6c..c7af34f6b1 100644 --- a/tests/rtc-test.c +++ b/tests/rtc-test.c @@ -15,7 +15,8 @@ #include "libqtest-single.h" #include "qemu/timer.h" -#include "hw/timer/mc146818rtc_regs.h" +#include "hw/rtc/mc146818rtc.h" +#include "hw/rtc/mc146818rtc_regs.h" #define UIP_HOLD_LENGTH (8 * NANOSECONDS_PER_SECOND / 32768) diff --git a/tests/test-block-iothread.c b/tests/test-block-iothread.c index cfe30bab21..0c861809f0 100644 --- a/tests/test-block-iothread.c +++ b/tests/test-block-iothread.c @@ -45,7 +45,7 @@ static int coroutine_fn bdrv_test_co_pdiscard(BlockDriverState *bs, } static int coroutine_fn -bdrv_test_co_truncate(BlockDriverState *bs, int64_t offset, +bdrv_test_co_truncate(BlockDriverState *bs, int64_t offset, bool exact, PreallocMode prealloc, Error **errp) { return 0; @@ -185,18 +185,18 @@ static void test_sync_op_truncate(BdrvChild *c) int ret; /* Normal success path */ - ret = bdrv_truncate(c, 65536, PREALLOC_MODE_OFF, NULL); + ret = bdrv_truncate(c, 65536, false, PREALLOC_MODE_OFF, NULL); g_assert_cmpint(ret, ==, 0); /* Early error: Negative offset */ - ret = bdrv_truncate(c, -2, PREALLOC_MODE_OFF, NULL); + ret = bdrv_truncate(c, -2, false, PREALLOC_MODE_OFF, NULL); g_assert_cmpint(ret, ==, -EINVAL); /* Error: Read-only image */ c->bs->read_only = true; c->bs->open_flags &= ~BDRV_O_RDWR; - ret = bdrv_truncate(c, 65536, PREALLOC_MODE_OFF, NULL); + ret = bdrv_truncate(c, 65536, false, PREALLOC_MODE_OFF, NULL); g_assert_cmpint(ret, ==, -EACCES); c->bs->read_only = false; diff --git a/tests/vhost-user-bridge.c b/tests/vhost-user-bridge.c index c4e350e1f5..6c3d490611 100644 --- a/tests/vhost-user-bridge.c +++ b/tests/vhost-user-bridge.c @@ -468,8 +468,8 @@ vubr_queue_set_started(VuDev *dev, int qidx, bool started) if (started && vubr->notifier.fd >= 0) { vu_set_queue_host_notifier(dev, vq, vubr->notifier.fd, - getpagesize(), - qidx * getpagesize()); + qemu_real_host_page_size, + qidx * qemu_real_host_page_size); } if (qidx % 2 == 1) { @@ -594,7 +594,7 @@ static void *notifier_thread(void *arg) { VuDev *dev = (VuDev *)arg; VubrDev *vubr = container_of(dev, VubrDev, vudev); - int pagesize = getpagesize(); + int pagesize = qemu_real_host_page_size; int qidx; while (true) { @@ -630,7 +630,7 @@ vubr_host_notifier_setup(VubrDev *dev) void *addr; int fd; - length = getpagesize() * VHOST_USER_BRIDGE_MAX_QUEUES; + length = qemu_real_host_page_size * VHOST_USER_BRIDGE_MAX_QUEUES; fd = mkstemp(template); if (fd < 0) { diff --git a/tests/virtio-blk-test.c b/tests/virtio-blk-test.c index ed13167392..2a23698211 100644 --- a/tests/virtio-blk-test.c +++ b/tests/virtio-blk-test.c @@ -113,21 +113,18 @@ static uint64_t virtio_blk_request(QGuestAllocator *alloc, QVirtioDevice *d, return addr; } -static void test_basic(QVirtioDevice *dev, QGuestAllocator *alloc, - QVirtQueue *vq) +/* Returns the request virtqueue so the caller can perform further tests */ +static QVirtQueue *test_basic(QVirtioDevice *dev, QGuestAllocator *alloc) { QVirtioBlkReq req; uint64_t req_addr; uint64_t capacity; - uint32_t features; + uint64_t features; uint32_t free_head; uint8_t status; char *data; QTestState *qts = global_qtest; - - capacity = qvirtio_config_readq(dev, 0); - - g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); + QVirtQueue *vq; features = qvirtio_get_features(dev); features = features & ~(QVIRTIO_F_BAD_FEATURE | @@ -136,6 +133,11 @@ static void test_basic(QVirtioDevice *dev, QGuestAllocator *alloc, (1u << VIRTIO_BLK_F_SCSI)); qvirtio_set_features(dev, features); + capacity = qvirtio_config_readq(dev, 0); + g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); + + vq = qvirtqueue_setup(dev, alloc, 0); + qvirtio_set_driver_ok(dev); /* Write and read with 3 descriptor layout */ @@ -332,14 +334,16 @@ static void test_basic(QVirtioDevice *dev, QGuestAllocator *alloc, guest_free(alloc, req_addr); } + + return vq; } static void basic(void *obj, void *data, QGuestAllocator *t_alloc) { QVirtioBlk *blk_if = obj; QVirtQueue *vq; - vq = qvirtqueue_setup(blk_if->vdev, t_alloc, 0); - test_basic(blk_if->vdev, t_alloc, vq); + + vq = test_basic(blk_if->vdev, t_alloc); qvirtqueue_cleanup(blk_if->vdev->bus, vq, t_alloc); } @@ -353,15 +357,12 @@ static void indirect(void *obj, void *u_data, QGuestAllocator *t_alloc) QVRingIndirectDesc *indirect; uint64_t req_addr; uint64_t capacity; - uint32_t features; + uint64_t features; uint32_t free_head; uint8_t status; char *data; QTestState *qts = global_qtest; - capacity = qvirtio_config_readq(dev, 0); - g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); - features = qvirtio_get_features(dev); g_assert_cmphex(features & (1u << VIRTIO_RING_F_INDIRECT_DESC), !=, 0); features = features & ~(QVIRTIO_F_BAD_FEATURE | @@ -369,6 +370,9 @@ static void indirect(void *obj, void *u_data, QGuestAllocator *t_alloc) (1u << VIRTIO_BLK_F_SCSI)); qvirtio_set_features(dev, features); + capacity = qvirtio_config_readq(dev, 0); + g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); + vq = qvirtqueue_setup(dev, t_alloc, 0); qvirtio_set_driver_ok(dev); @@ -384,8 +388,8 @@ static void indirect(void *obj, void *u_data, QGuestAllocator *t_alloc) g_free(req.data); indirect = qvring_indirect_desc_setup(qts, dev, t_alloc, 2); - qvring_indirect_desc_add(qts, indirect, req_addr, 528, false); - qvring_indirect_desc_add(qts, indirect, req_addr + 528, 1, true); + qvring_indirect_desc_add(dev, qts, indirect, req_addr, 528, false); + qvring_indirect_desc_add(dev, qts, indirect, req_addr + 528, 1, true); free_head = qvirtqueue_add_indirect(qts, vq, indirect); qvirtqueue_kick(qts, dev, vq, free_head); @@ -409,8 +413,8 @@ static void indirect(void *obj, void *u_data, QGuestAllocator *t_alloc) g_free(req.data); indirect = qvring_indirect_desc_setup(qts, dev, t_alloc, 2); - qvring_indirect_desc_add(qts, indirect, req_addr, 16, false); - qvring_indirect_desc_add(qts, indirect, req_addr + 16, 513, true); + qvring_indirect_desc_add(dev, qts, indirect, req_addr, 16, false); + qvring_indirect_desc_add(dev, qts, indirect, req_addr + 16, 513, true); free_head = qvirtqueue_add_indirect(qts, vq, indirect); qvirtqueue_kick(qts, dev, vq, free_head); @@ -434,8 +438,16 @@ static void config(void *obj, void *data, QGuestAllocator *t_alloc) QVirtioBlk *blk_if = obj; QVirtioDevice *dev = blk_if->vdev; int n_size = TEST_IMAGE_SIZE / 2; + uint64_t features; uint64_t capacity; + features = qvirtio_get_features(dev); + features = features & ~(QVIRTIO_F_BAD_FEATURE | + (1u << VIRTIO_RING_F_INDIRECT_DESC) | + (1u << VIRTIO_RING_F_EVENT_IDX) | + (1u << VIRTIO_BLK_F_SCSI)); + qvirtio_set_features(dev, features); + capacity = qvirtio_config_readq(dev, 0); g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); @@ -460,7 +472,7 @@ static void msix(void *obj, void *u_data, QGuestAllocator *t_alloc) int n_size = TEST_IMAGE_SIZE / 2; uint64_t req_addr; uint64_t capacity; - uint32_t features; + uint64_t features; uint32_t free_head; uint8_t status; char *data; @@ -475,9 +487,6 @@ static void msix(void *obj, void *u_data, QGuestAllocator *t_alloc) qpci_msix_enable(pdev->pdev); qvirtio_pci_set_msix_configuration_vector(pdev, t_alloc, 0); - capacity = qvirtio_config_readq(dev, 0); - g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); - features = qvirtio_get_features(dev); features = features & ~(QVIRTIO_F_BAD_FEATURE | (1u << VIRTIO_RING_F_INDIRECT_DESC) | @@ -485,6 +494,9 @@ static void msix(void *obj, void *u_data, QGuestAllocator *t_alloc) (1u << VIRTIO_BLK_F_SCSI)); qvirtio_set_features(dev, features); + capacity = qvirtio_config_readq(dev, 0); + g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); + vq = qvirtqueue_setup(dev, t_alloc, 0); qvirtqueue_pci_msix_setup(pdev, (QVirtQueuePCI *)vq, t_alloc, 1); @@ -567,7 +579,7 @@ static void idx(void *obj, void *u_data, QGuestAllocator *t_alloc) QVirtioBlkReq req; uint64_t req_addr; uint64_t capacity; - uint32_t features; + uint64_t features; uint32_t free_head; uint32_t write_head; uint32_t desc_idx; @@ -584,9 +596,6 @@ static void idx(void *obj, void *u_data, QGuestAllocator *t_alloc) qpci_msix_enable(pdev->pdev); qvirtio_pci_set_msix_configuration_vector(pdev, t_alloc, 0); - capacity = qvirtio_config_readq(dev, 0); - g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); - features = qvirtio_get_features(dev); features = features & ~(QVIRTIO_F_BAD_FEATURE | (1u << VIRTIO_RING_F_INDIRECT_DESC) | @@ -594,6 +603,9 @@ static void idx(void *obj, void *u_data, QGuestAllocator *t_alloc) (1u << VIRTIO_BLK_F_SCSI)); qvirtio_set_features(dev, features); + capacity = qvirtio_config_readq(dev, 0); + g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); + vq = qvirtqueue_setup(dev, t_alloc, 0); qvirtqueue_pci_msix_setup(pdev, (QVirtQueuePCI *)vq, t_alloc, 1); @@ -739,9 +751,7 @@ static void resize(void *obj, void *data, QGuestAllocator *t_alloc) QVirtQueue *vq; QTestState *qts = global_qtest; - vq = qvirtqueue_setup(dev, t_alloc, 0); - - test_basic(dev, t_alloc, vq); + vq = test_basic(dev, t_alloc); qmp_discard_response("{ 'execute': 'block_resize', " " 'arguments': { 'device': 'drive0', " diff --git a/tests/virtio-scsi-test.c b/tests/virtio-scsi-test.c index 7c8f9b27f8..0415e75876 100644 --- a/tests/virtio-scsi-test.c +++ b/tests/virtio-scsi-test.c @@ -123,10 +123,16 @@ static QVirtioSCSIQueues *qvirtio_scsi_init(QVirtioDevice *dev) QVirtioSCSIQueues *vs; const uint8_t test_unit_ready_cdb[VIRTIO_SCSI_CDB_SIZE] = {}; struct virtio_scsi_cmd_resp resp; + uint64_t features; int i; vs = g_new0(QVirtioSCSIQueues, 1); vs->dev = dev; + + features = qvirtio_get_features(dev); + features &= ~(QVIRTIO_F_BAD_FEATURE | (1ull << VIRTIO_RING_F_EVENT_IDX)); + qvirtio_set_features(dev, features); + vs->num_queues = qvirtio_config_readl(dev, 0); g_assert_cmpint(vs->num_queues, <, MAX_NUM_QUEUES); @@ -135,6 +141,8 @@ static QVirtioSCSIQueues *qvirtio_scsi_init(QVirtioDevice *dev) vs->vq[i] = qvirtqueue_setup(dev, alloc, i); } + qvirtio_set_driver_ok(dev); + /* Clear the POWER ON OCCURRED unit attention */ g_assert_cmpint(virtio_scsi_do_command(vs, test_unit_ready_cdb, NULL, 0, NULL, 0, &resp), diff --git a/tests/vm/basevm.py b/tests/vm/basevm.py index b5d1479bee..2929de23aa 100755 --- a/tests/vm/basevm.py +++ b/tests/vm/basevm.py @@ -57,6 +57,8 @@ class BaseVM(object): arch = "#arch" # command to halt the guest, can be overridden by subclasses poweroff = "poweroff" + # enable IPv6 networking + ipv6 = True def __init__(self, debug=False, vcpus=None): self._guest = None self._tmpdir = os.path.realpath(tempfile.mkdtemp(prefix="vm-test-", @@ -81,7 +83,8 @@ class BaseVM(object): self._args = [ \ "-nodefaults", "-m", "4G", "-cpu", "max", - "-netdev", "user,id=vnet,hostfwd=:127.0.0.1:0-:22", + "-netdev", "user,id=vnet,hostfwd=:127.0.0.1:0-:22" + + (",ipv6=no" if not self.ipv6 else ""), "-device", "virtio-net-pci,netdev=vnet", "-vnc", "127.0.0.1:0,to=20"] if vcpus and vcpus > 1: diff --git a/tests/vm/netbsd b/tests/vm/netbsd index ee9eaeab50..18aa56ae82 100755 --- a/tests/vm/netbsd +++ b/tests/vm/netbsd @@ -28,6 +28,13 @@ class NetBSDVM(basevm.BaseVM): gmake --output-sync -j{jobs} {target} {verbose}; """ + # Workaround for NetBSD + IPv6 + slirp issues. + # NetBSD seems to ignore the ICMPv6 Destination Unreachable + # messages generated by slirp. When the host has no IPv6 + # connectivity, this causes every connection to ftp.NetBSD.org + # take more than a minute to be established. + ipv6 = False + def build_image(self, img): cimg = self._download_with_cache("http://download.patchew.org/netbsd-7.1-amd64.img.xz", sha256sum='b633d565b0eac3d02015cd0c81440bd8a7a8df8512615ac1ee05d318be015732') |