diff options
91 files changed, 3222 insertions, 1477 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 922945c..cce37e7 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -553,6 +553,8 @@ Tracing M: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com> S: Maintained F: trace/ +F: scripts/tracetool.py +F: scripts/tracetool/ F: docs/tracing.txt T: git://github.com/stefanha/qemu.git tracing diff --git a/Makefile.objs b/Makefile.objs index 5c3bcda..6d6f24d 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -378,12 +378,12 @@ else trace.h: trace.h-timestamp endif trace.h-timestamp: $(SRC_PATH)/trace-events $(BUILD_DIR)/config-host.mak - $(call quiet-command,sh $(SRC_PATH)/scripts/tracetool --$(TRACE_BACKEND) -h < $< > $@," GEN trace.h") + $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/tracetool.py --format=h --backend=$(TRACE_BACKEND) < $< > $@," GEN trace.h") @cmp -s $@ trace.h || cp $@ trace.h trace.c: trace.c-timestamp trace.c-timestamp: $(SRC_PATH)/trace-events $(BUILD_DIR)/config-host.mak - $(call quiet-command,sh $(SRC_PATH)/scripts/tracetool --$(TRACE_BACKEND) -c < $< > $@," GEN trace.c") + $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/tracetool.py --format=c --backend=$(TRACE_BACKEND) < $< > $@," GEN trace.c") @cmp -s $@ trace.c || cp $@ trace.c trace.o: trace.c $(GENERATED_HEADERS) @@ -396,7 +396,7 @@ trace-dtrace.h: trace-dtrace.dtrace # rule file. So we use '.dtrace' instead trace-dtrace.dtrace: trace-dtrace.dtrace-timestamp trace-dtrace.dtrace-timestamp: $(SRC_PATH)/trace-events $(BUILD_DIR)/config-host.mak - $(call quiet-command,sh $(SRC_PATH)/scripts/tracetool --$(TRACE_BACKEND) -d < $< > $@," GEN trace-dtrace.dtrace") + $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/tracetool.py --format=d --backend=$(TRACE_BACKEND) < $< > $@," GEN trace-dtrace.dtrace") @cmp -s $@ trace-dtrace.dtrace || cp $@ trace-dtrace.dtrace trace-dtrace.o: trace-dtrace.dtrace $(GENERATED_HEADERS) diff --git a/Makefile.target b/Makefile.target index b6a9330..7eda443 100644 --- a/Makefile.target +++ b/Makefile.target @@ -59,12 +59,13 @@ TARGET_TYPE=system endif $(QEMU_PROG).stp: $(SRC_PATH)/trace-events - $(call quiet-command,sh $(SRC_PATH)/scripts/tracetool \ - --$(TRACE_BACKEND) \ - --binary $(bindir)/$(QEMU_PROG) \ - --target-arch $(TARGET_ARCH) \ - --target-type $(TARGET_TYPE) \ - --stap < $(SRC_PATH)/trace-events > $(QEMU_PROG).stp," GEN $(QEMU_PROG).stp") + $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/tracetool.py \ + --format=stap \ + --backend=$(TRACE_BACKEND) \ + --binary=$(bindir)/$(QEMU_PROG) \ + --target-arch=$(TARGET_ARCH) \ + --target-type=$(TARGET_TYPE) \ + < $(SRC_PATH)/trace-events > $(QEMU_PROG).stp," GEN $(QEMU_PROG).stp") else stap: endif @@ -35,7 +35,6 @@ struct AioHandler IOHandler *io_read; IOHandler *io_write; AioFlushHandler *io_flush; - AioProcessQueue *io_process_queue; int deleted; void *opaque; QLIST_ENTRY(AioHandler) node; @@ -58,7 +57,6 @@ int qemu_aio_set_fd_handler(int fd, IOHandler *io_read, IOHandler *io_write, AioFlushHandler *io_flush, - AioProcessQueue *io_process_queue, void *opaque) { AioHandler *node; @@ -91,7 +89,6 @@ int qemu_aio_set_fd_handler(int fd, node->io_read = io_read; node->io_write = io_write; node->io_flush = io_flush; - node->io_process_queue = io_process_queue; node->opaque = opaque; } @@ -102,131 +99,96 @@ int qemu_aio_set_fd_handler(int fd, void qemu_aio_flush(void) { - AioHandler *node; - int ret; - - do { - ret = 0; - - /* - * If there are pending emulated aio start them now so flush - * will be able to return 1. - */ - qemu_aio_wait(); - - QLIST_FOREACH(node, &aio_handlers, node) { - if (node->io_flush) { - ret |= node->io_flush(node->opaque); - } - } - } while (qemu_bh_poll() || ret > 0); + while (qemu_aio_wait()); } -int qemu_aio_process_queue(void) +bool qemu_aio_wait(void) { AioHandler *node; - int ret = 0; + fd_set rdfds, wrfds; + int max_fd = -1; + int ret; + bool busy; + + /* + * If there are callbacks left that have been queued, we need to call then. + * Do not call select in this case, because it is possible that the caller + * does not need a complete flush (as is the case for qemu_aio_wait loops). + */ + if (qemu_bh_poll()) { + return true; + } walking_handlers = 1; + FD_ZERO(&rdfds); + FD_ZERO(&wrfds); + + /* fill fd sets */ + busy = false; QLIST_FOREACH(node, &aio_handlers, node) { - if (node->io_process_queue) { - if (node->io_process_queue(node->opaque)) { - ret = 1; + /* If there aren't pending AIO operations, don't invoke callbacks. + * Otherwise, if there are no AIO requests, qemu_aio_wait() would + * wait indefinitely. + */ + if (node->io_flush) { + if (node->io_flush(node->opaque) == 0) { + continue; } + busy = true; + } + if (!node->deleted && node->io_read) { + FD_SET(node->fd, &rdfds); + max_fd = MAX(max_fd, node->fd + 1); + } + if (!node->deleted && node->io_write) { + FD_SET(node->fd, &wrfds); + max_fd = MAX(max_fd, node->fd + 1); } } walking_handlers = 0; - return ret; -} - -void qemu_aio_wait(void) -{ - int ret; - - if (qemu_bh_poll()) - return; - - /* - * If there are callbacks left that have been queued, we need to call then. - * Return afterwards to avoid waiting needlessly in select(). - */ - if (qemu_aio_process_queue()) - return; + /* No AIO operations? Get us out of here */ + if (!busy) { + return false; + } - do { - AioHandler *node; - fd_set rdfds, wrfds; - int max_fd = -1; + /* wait until next event */ + ret = select(max_fd, &rdfds, &wrfds, NULL, NULL); + /* if we have any readable fds, dispatch event */ + if (ret > 0) { walking_handlers = 1; - FD_ZERO(&rdfds); - FD_ZERO(&wrfds); + /* we have to walk very carefully in case + * qemu_aio_set_fd_handler is called while we're walking */ + node = QLIST_FIRST(&aio_handlers); + while (node) { + AioHandler *tmp; - /* fill fd sets */ - QLIST_FOREACH(node, &aio_handlers, node) { - /* If there aren't pending AIO operations, don't invoke callbacks. - * Otherwise, if there are no AIO requests, qemu_aio_wait() would - * wait indefinitely. - */ - if (node->io_flush && node->io_flush(node->opaque) == 0) - continue; - - if (!node->deleted && node->io_read) { - FD_SET(node->fd, &rdfds); - max_fd = MAX(max_fd, node->fd + 1); + if (!node->deleted && + FD_ISSET(node->fd, &rdfds) && + node->io_read) { + node->io_read(node->opaque); } - if (!node->deleted && node->io_write) { - FD_SET(node->fd, &wrfds); - max_fd = MAX(max_fd, node->fd + 1); + if (!node->deleted && + FD_ISSET(node->fd, &wrfds) && + node->io_write) { + node->io_write(node->opaque); } - } - walking_handlers = 0; + tmp = node; + node = QLIST_NEXT(node, node); - /* No AIO operations? Get us out of here */ - if (max_fd == -1) - break; - - /* wait until next event */ - ret = select(max_fd, &rdfds, &wrfds, NULL, NULL); - if (ret == -1 && errno == EINTR) - continue; - - /* if we have any readable fds, dispatch event */ - if (ret > 0) { - walking_handlers = 1; - - /* we have to walk very carefully in case - * qemu_aio_set_fd_handler is called while we're walking */ - node = QLIST_FIRST(&aio_handlers); - while (node) { - AioHandler *tmp; - - if (!node->deleted && - FD_ISSET(node->fd, &rdfds) && - node->io_read) { - node->io_read(node->opaque); - } - if (!node->deleted && - FD_ISSET(node->fd, &wrfds) && - node->io_write) { - node->io_write(node->opaque); - } - - tmp = node; - node = QLIST_NEXT(node, node); - - if (tmp->deleted) { - QLIST_REMOVE(tmp, node); - g_free(tmp); - } + if (tmp->deleted) { + QLIST_REMOVE(tmp, node); + g_free(tmp); } - - walking_handlers = 0; } - } while (ret == 0); + + walking_handlers = 0; + } + + return true; } @@ -80,6 +80,8 @@ static BlockDriverAIOCB *bdrv_co_aio_rw_vector(BlockDriverState *bs, void *opaque, bool is_write); static void coroutine_fn bdrv_co_do_rw(void *opaque); +static int coroutine_fn bdrv_co_do_write_zeroes(BlockDriverState *bs, + int64_t sector_num, int nb_sectors); static bool bdrv_exceed_bps_limits(BlockDriverState *bs, int nb_sectors, bool is_write, double elapsed_time, uint64_t *wait); @@ -812,10 +814,13 @@ unlink_and_fail: void bdrv_close(BlockDriverState *bs) { + bdrv_flush(bs); if (bs->drv) { if (bs->job) { block_job_cancel_sync(bs->job); } + bdrv_drain_all(); + if (bs == bs_snapshots) { bs_snapshots = NULL; } @@ -1705,8 +1710,8 @@ static int coroutine_fn bdrv_co_do_copy_on_readv(BlockDriverState *bs, if (drv->bdrv_co_write_zeroes && buffer_is_zero(bounce_buffer, iov.iov_len)) { - ret = drv->bdrv_co_write_zeroes(bs, cluster_sector_num, - cluster_nb_sectors); + ret = bdrv_co_do_write_zeroes(bs, cluster_sector_num, + cluster_nb_sectors); } else { ret = drv->bdrv_co_writev(bs, cluster_sector_num, cluster_nb_sectors, &bounce_qiov); @@ -1816,9 +1821,15 @@ static int coroutine_fn bdrv_co_do_write_zeroes(BlockDriverState *bs, struct iovec iov; int ret; + /* TODO Emulate only part of misaligned requests instead of letting block + * drivers return -ENOTSUP and emulate everything */ + /* First try the efficient write zeroes operation */ if (drv->bdrv_co_write_zeroes) { - return drv->bdrv_co_write_zeroes(bs, sector_num, nb_sectors); + ret = drv->bdrv_co_write_zeroes(bs, sector_num, nb_sectors); + if (ret != -ENOTSUP) { + return ret; + } } /* Fall back to bounce buffer if write zeroes is unsupported */ diff --git a/block/cow.c b/block/cow.c index 8d3c9f8..a5a00eb 100644 --- a/block/cow.c +++ b/block/cow.c @@ -103,7 +103,7 @@ static int cow_open(BlockDriverState *bs, int flags) } /* - * XXX(hch): right now these functions are extremely ineffcient. + * XXX(hch): right now these functions are extremely inefficient. * We should just read the whole bitmap we'll need in one go instead. */ static inline int cow_set_bit(BlockDriverState *bs, int64_t bitnum) diff --git a/block/curl.c b/block/curl.c index a909eca..bf3680b 100644 --- a/block/curl.c +++ b/block/curl.c @@ -89,19 +89,17 @@ static int curl_sock_cb(CURL *curl, curl_socket_t fd, int action, DPRINTF("CURL (AIO): Sock action %d on fd %d\n", action, fd); switch (action) { case CURL_POLL_IN: - qemu_aio_set_fd_handler(fd, curl_multi_do, NULL, curl_aio_flush, - NULL, s); + qemu_aio_set_fd_handler(fd, curl_multi_do, NULL, curl_aio_flush, s); break; case CURL_POLL_OUT: - qemu_aio_set_fd_handler(fd, NULL, curl_multi_do, curl_aio_flush, - NULL, s); + qemu_aio_set_fd_handler(fd, NULL, curl_multi_do, curl_aio_flush, s); break; case CURL_POLL_INOUT: qemu_aio_set_fd_handler(fd, curl_multi_do, curl_multi_do, - curl_aio_flush, NULL, s); + curl_aio_flush, s); break; case CURL_POLL_REMOVE: - qemu_aio_set_fd_handler(fd, NULL, NULL, NULL, NULL, NULL); + qemu_aio_set_fd_handler(fd, NULL, NULL, NULL, NULL); break; } diff --git a/block/iscsi.c b/block/iscsi.c index bd3ca11..5222726 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -108,7 +108,7 @@ iscsi_set_events(IscsiLun *iscsilun) qemu_aio_set_fd_handler(iscsi_get_fd(iscsi), iscsi_process_read, (iscsi_which_events(iscsi) & POLLOUT) ? iscsi_process_write : NULL, - iscsi_process_flush, NULL, iscsilun); + iscsi_process_flush, iscsilun); } static void @@ -682,7 +682,7 @@ static void iscsi_close(BlockDriverState *bs) IscsiLun *iscsilun = bs->opaque; struct iscsi_context *iscsi = iscsilun->iscsi; - qemu_aio_set_fd_handler(iscsi_get_fd(iscsi), NULL, NULL, NULL, NULL, NULL); + qemu_aio_set_fd_handler(iscsi_get_fd(iscsi), NULL, NULL, NULL, NULL); iscsi_destroy_context(iscsi); memset(iscsilun, 0, sizeof(IscsiLun)); } diff --git a/block/nbd.c b/block/nbd.c index 161b299..56dbf6e 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -150,11 +150,19 @@ static int nbd_have_request(void *opaque) static void nbd_reply_ready(void *opaque) { BDRVNBDState *s = opaque; - int i; + uint64_t i; + int ret; if (s->reply.handle == 0) { - /* No reply already in flight. Fetch a header. */ - if (nbd_receive_reply(s->sock, &s->reply) < 0) { + /* No reply already in flight. Fetch a header. It is possible + * that another thread has done the same thing in parallel, so + * the socket is not readable anymore. + */ + ret = nbd_receive_reply(s->sock, &s->reply); + if (ret == -EAGAIN) { + return; + } + if (ret < 0) { s->reply.handle = 0; goto fail; } @@ -164,6 +172,10 @@ static void nbd_reply_ready(void *opaque) * handler acts as a synchronization point and ensures that only * one coroutine is called until the reply finishes. */ i = HANDLE_TO_INDEX(s, s->reply.handle); + if (i >= MAX_NBD_REQUESTS) { + goto fail; + } + if (s->recv_coroutine[i]) { qemu_coroutine_enter(s->recv_coroutine[i], NULL); return; @@ -191,17 +203,16 @@ static int nbd_co_send_request(BDRVNBDState *s, struct nbd_request *request, qemu_co_mutex_lock(&s->send_mutex); s->send_coroutine = qemu_coroutine_self(); qemu_aio_set_fd_handler(s->sock, nbd_reply_ready, nbd_restart_write, - nbd_have_request, NULL, s); + nbd_have_request, s); rc = nbd_send_request(s->sock, request); - if (rc != -1 && iov) { + if (rc >= 0 && iov) { ret = qemu_co_sendv(s->sock, iov, request->len, offset); if (ret != request->len) { - errno = -EIO; - rc = -1; + return -EIO; } } qemu_aio_set_fd_handler(s->sock, nbd_reply_ready, NULL, - nbd_have_request, NULL, s); + nbd_have_request, s); s->send_coroutine = NULL; qemu_co_mutex_unlock(&s->send_mutex); return rc; @@ -256,7 +267,7 @@ static int nbd_establish_connection(BlockDriverState *bs) } /* Failed to establish connection */ - if (sock == -1) { + if (sock < 0) { logout("Failed to establish connection to NBD server\n"); return -errno; } @@ -264,17 +275,17 @@ static int nbd_establish_connection(BlockDriverState *bs) /* NBD handshake */ ret = nbd_receive_negotiate(sock, s->export_name, &s->nbdflags, &size, &blocksize); - if (ret == -1) { + if (ret < 0) { logout("Failed to negotiate with the NBD server\n"); closesocket(sock); - return -errno; + return ret; } /* Now that we're connected, set the socket to be non-blocking and * kick the reply mechanism. */ socket_set_nonblock(sock); qemu_aio_set_fd_handler(s->sock, nbd_reply_ready, NULL, - nbd_have_request, NULL, s); + nbd_have_request, s); s->sock = sock; s->size = size; @@ -294,7 +305,7 @@ static void nbd_teardown_connection(BlockDriverState *bs) request.len = 0; nbd_send_request(s->sock, &request); - qemu_aio_set_fd_handler(s->sock, NULL, NULL, NULL, NULL, NULL); + qemu_aio_set_fd_handler(s->sock, NULL, NULL, NULL, NULL); closesocket(s->sock); } @@ -327,14 +338,16 @@ static int nbd_co_readv_1(BlockDriverState *bs, int64_t sector_num, BDRVNBDState *s = bs->opaque; struct nbd_request request; struct nbd_reply reply; + ssize_t ret; request.type = NBD_CMD_READ; request.from = sector_num * 512; request.len = nb_sectors * 512; nbd_coroutine_start(s, &request); - if (nbd_co_send_request(s, &request, NULL, 0) == -1) { - reply.error = errno; + ret = nbd_co_send_request(s, &request, NULL, 0); + if (ret < 0) { + reply.error = -ret; } else { nbd_co_receive_reply(s, &request, &reply, qiov->iov, offset); } @@ -350,6 +363,7 @@ static int nbd_co_writev_1(BlockDriverState *bs, int64_t sector_num, BDRVNBDState *s = bs->opaque; struct nbd_request request; struct nbd_reply reply; + ssize_t ret; request.type = NBD_CMD_WRITE; if (!bdrv_enable_write_cache(bs) && (s->nbdflags & NBD_FLAG_SEND_FUA)) { @@ -360,8 +374,9 @@ static int nbd_co_writev_1(BlockDriverState *bs, int64_t sector_num, request.len = nb_sectors * 512; nbd_coroutine_start(s, &request); - if (nbd_co_send_request(s, &request, qiov->iov, offset) == -1) { - reply.error = errno; + ret = nbd_co_send_request(s, &request, qiov->iov, offset); + if (ret < 0) { + reply.error = -ret; } else { nbd_co_receive_reply(s, &request, &reply, NULL, 0); } @@ -412,6 +427,7 @@ static int nbd_co_flush(BlockDriverState *bs) BDRVNBDState *s = bs->opaque; struct nbd_request request; struct nbd_reply reply; + ssize_t ret; if (!(s->nbdflags & NBD_FLAG_SEND_FLUSH)) { return 0; @@ -426,8 +442,9 @@ static int nbd_co_flush(BlockDriverState *bs) request.len = 0; nbd_coroutine_start(s, &request); - if (nbd_co_send_request(s, &request, NULL, 0) == -1) { - reply.error = errno; + ret = nbd_co_send_request(s, &request, NULL, 0); + if (ret < 0) { + reply.error = -ret; } else { nbd_co_receive_reply(s, &request, &reply, NULL, 0); } @@ -441,6 +458,7 @@ static int nbd_co_discard(BlockDriverState *bs, int64_t sector_num, BDRVNBDState *s = bs->opaque; struct nbd_request request; struct nbd_reply reply; + ssize_t ret; if (!(s->nbdflags & NBD_FLAG_SEND_TRIM)) { return 0; @@ -450,8 +468,9 @@ static int nbd_co_discard(BlockDriverState *bs, int64_t sector_num, request.len = nb_sectors * 512; nbd_coroutine_start(s, &request); - if (nbd_co_send_request(s, &request, NULL, 0) == -1) { - reply.error = errno; + ret = nbd_co_send_request(s, &request, NULL, 0); + if (ret < 0) { + reply.error = -ret; } else { nbd_co_receive_reply(s, &request, &reply, NULL, 0); } diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index cbd224d..a747a88 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -195,7 +195,7 @@ static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table) l2_table = *table; - if (old_l2_offset == 0) { + if ((old_l2_offset & L1E_OFFSET_MASK) == 0) { /* if there was no old l2 table, clear the new table */ memset(l2_table, 0, s->l2_size * sizeof(uint64_t)); } else { @@ -203,7 +203,8 @@ static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table) /* if there was an old l2 table, read it from the disk */ BLKDBG_EVENT(bs->file, BLKDBG_L2_ALLOC_COW_READ); - ret = qcow2_cache_get(bs, s->l2_table_cache, old_l2_offset, + ret = qcow2_cache_get(bs, s->l2_table_cache, + old_l2_offset & L1E_OFFSET_MASK, (void**) &old_table); if (ret < 0) { goto fail; @@ -246,28 +247,44 @@ fail: return ret; } +/* + * Checks how many clusters in a given L2 table are contiguous in the image + * file. As soon as one of the flags in the bitmask stop_flags changes compared + * to the first cluster, the search is stopped and the cluster is not counted + * as contiguous. (This allows it, for example, to stop at the first compressed + * cluster which may require a different handling) + */ static int count_contiguous_clusters(uint64_t nb_clusters, int cluster_size, - uint64_t *l2_table, uint64_t start, uint64_t mask) + uint64_t *l2_table, uint64_t start, uint64_t stop_flags) { int i; - uint64_t offset = be64_to_cpu(l2_table[0]) & ~mask; + uint64_t mask = stop_flags | L2E_OFFSET_MASK; + uint64_t offset = be64_to_cpu(l2_table[0]) & mask; if (!offset) return 0; - for (i = start; i < start + nb_clusters; i++) - if (offset + (uint64_t) i * cluster_size != (be64_to_cpu(l2_table[i]) & ~mask)) + for (i = start; i < start + nb_clusters; i++) { + uint64_t l2_entry = be64_to_cpu(l2_table[i]) & mask; + if (offset + (uint64_t) i * cluster_size != l2_entry) { break; + } + } return (i - start); } static int count_contiguous_free_clusters(uint64_t nb_clusters, uint64_t *l2_table) { - int i = 0; + int i; + + for (i = 0; i < nb_clusters; i++) { + int type = qcow2_get_cluster_type(be64_to_cpu(l2_table[i])); - while(nb_clusters-- && l2_table[i] == 0) - i++; + if (type != QCOW2_CLUSTER_UNALLOCATED) { + break; + } + } return i; } @@ -367,11 +384,9 @@ out: * * on exit, *num is the number of contiguous sectors we can read. * - * Return 0, if the offset is found - * Return -errno, otherwise. - * + * Returns the cluster type (QCOW2_CLUSTER_*) on success, -errno in error + * cases. */ - int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, int *num, uint64_t *cluster_offset) { @@ -407,19 +422,19 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, /* seek the the l2 offset in the l1 table */ l1_index = offset >> l1_bits; - if (l1_index >= s->l1_size) + if (l1_index >= s->l1_size) { + ret = QCOW2_CLUSTER_UNALLOCATED; goto out; + } - l2_offset = s->l1_table[l1_index]; - - /* seek the l2 table of the given l2 offset */ - - if (!l2_offset) + l2_offset = s->l1_table[l1_index] & L1E_OFFSET_MASK; + if (!l2_offset) { + ret = QCOW2_CLUSTER_UNALLOCATED; goto out; + } /* load the l2 table in memory */ - l2_offset &= ~QCOW_OFLAG_COPIED; ret = l2_load(bs, l2_offset, &l2_table); if (ret < 0) { return ret; @@ -431,26 +446,44 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, *cluster_offset = be64_to_cpu(l2_table[l2_index]); nb_clusters = size_to_clusters(s, nb_needed << 9); - if (!*cluster_offset) { + ret = qcow2_get_cluster_type(*cluster_offset); + switch (ret) { + case QCOW2_CLUSTER_COMPRESSED: + /* Compressed clusters can only be processed one by one */ + c = 1; + *cluster_offset &= L2E_COMPRESSED_OFFSET_SIZE_MASK; + break; + case QCOW2_CLUSTER_ZERO: + c = count_contiguous_clusters(nb_clusters, s->cluster_size, + &l2_table[l2_index], 0, + QCOW_OFLAG_COMPRESSED | QCOW_OFLAG_ZERO); + *cluster_offset = 0; + break; + case QCOW2_CLUSTER_UNALLOCATED: /* how many empty clusters ? */ c = count_contiguous_free_clusters(nb_clusters, &l2_table[l2_index]); - } else { + *cluster_offset = 0; + break; + case QCOW2_CLUSTER_NORMAL: /* how many allocated clusters ? */ c = count_contiguous_clusters(nb_clusters, s->cluster_size, - &l2_table[l2_index], 0, QCOW_OFLAG_COPIED); + &l2_table[l2_index], 0, + QCOW_OFLAG_COMPRESSED | QCOW_OFLAG_ZERO); + *cluster_offset &= L2E_OFFSET_MASK; + break; } qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); - nb_available = (c * s->cluster_sectors); + nb_available = (c * s->cluster_sectors); + out: if (nb_available > nb_needed) nb_available = nb_needed; *num = nb_available - index_in_cluster; - *cluster_offset &=~QCOW_OFLAG_COPIED; - return 0; + return ret; } /* @@ -483,13 +516,13 @@ static int get_cluster_table(BlockDriverState *bs, uint64_t offset, return ret; } } - l2_offset = s->l1_table[l1_index]; + + l2_offset = s->l1_table[l1_index] & L1E_OFFSET_MASK; /* seek the l2 table of the given l2 offset */ - if (l2_offset & QCOW_OFLAG_COPIED) { + if (s->l1_table[l1_index] & QCOW_OFLAG_COPIED) { /* load the l2 table in memory */ - l2_offset &= ~QCOW_OFLAG_COPIED; ret = l2_load(bs, l2_offset, &l2_table); if (ret < 0) { return ret; @@ -505,7 +538,7 @@ static int get_cluster_table(BlockDriverState *bs, uint64_t offset, if (l2_offset) { qcow2_free_clusters(bs, l2_offset, s->l2_size * sizeof(uint64_t)); } - l2_offset = s->l1_table[l1_index] & ~QCOW_OFLAG_COPIED; + l2_offset = s->l1_table[l1_index] & L1E_OFFSET_MASK; } /* find the cluster offset for the given disk offset */ @@ -546,15 +579,14 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, return 0; } + /* Compression can't overwrite anything. Fail if the cluster was already + * allocated. */ cluster_offset = be64_to_cpu(l2_table[l2_index]); - if (cluster_offset & QCOW_OFLAG_COPIED) { + if (cluster_offset & L2E_OFFSET_MASK) { qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); return 0; } - if (cluster_offset) - qcow2_free_any_clusters(bs, cluster_offset, 1); - cluster_offset = qcow2_alloc_bytes(bs, compressed_size); if (cluster_offset < 0) { qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); @@ -663,8 +695,7 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m) */ if (j != 0) { for (i = 0; i < j; i++) { - qcow2_free_any_clusters(bs, - be64_to_cpu(old_cluster[i]) & ~QCOW_OFLAG_COPIED, 1); + qcow2_free_any_clusters(bs, be64_to_cpu(old_cluster[i]), 1); } } @@ -682,29 +713,28 @@ err: static int count_cow_clusters(BDRVQcowState *s, int nb_clusters, uint64_t *l2_table, int l2_index) { - int i = 0; - uint64_t cluster_offset; + int i; - while (i < nb_clusters) { - i += count_contiguous_clusters(nb_clusters - i, s->cluster_size, - &l2_table[l2_index], i, 0); - if ((i >= nb_clusters) || be64_to_cpu(l2_table[l2_index + i])) { - break; - } + for (i = 0; i < nb_clusters; i++) { + uint64_t l2_entry = be64_to_cpu(l2_table[l2_index + i]); + int cluster_type = qcow2_get_cluster_type(l2_entry); - i += count_contiguous_free_clusters(nb_clusters - i, - &l2_table[l2_index + i]); - if (i >= nb_clusters) { + switch(cluster_type) { + case QCOW2_CLUSTER_NORMAL: + if (l2_entry & QCOW_OFLAG_COPIED) { + goto out; + } break; - } - - cluster_offset = be64_to_cpu(l2_table[l2_index + i]); - - if ((cluster_offset & QCOW_OFLAG_COPIED) || - (cluster_offset & QCOW_OFLAG_COMPRESSED)) + case QCOW2_CLUSTER_UNALLOCATED: + case QCOW2_CLUSTER_COMPRESSED: + case QCOW2_CLUSTER_ZERO: break; + default: + abort(); + } } +out: assert(i <= nb_clusters); return i; } @@ -842,10 +872,14 @@ again: * Check how many clusters are already allocated and don't need COW, and how * many need a new allocation. */ - if (cluster_offset & QCOW_OFLAG_COPIED) { + if (qcow2_get_cluster_type(cluster_offset) == QCOW2_CLUSTER_NORMAL + && (cluster_offset & QCOW_OFLAG_COPIED)) + { /* We keep all QCOW_OFLAG_COPIED clusters */ - keep_clusters = count_contiguous_clusters(nb_clusters, s->cluster_size, - &l2_table[l2_index], 0, 0); + keep_clusters = + count_contiguous_clusters(nb_clusters, s->cluster_size, + &l2_table[l2_index], 0, + QCOW_OFLAG_COPIED | QCOW_OFLAG_ZERO); assert(keep_clusters <= nb_clusters); nb_clusters -= keep_clusters; } else { @@ -860,7 +894,7 @@ again: cluster_offset = 0; } - cluster_offset &= ~QCOW_OFLAG_COPIED; + cluster_offset &= L2E_OFFSET_MASK; /* If there is something left to allocate, do that now */ *m = (QCowL2Meta) { @@ -931,7 +965,7 @@ again: fail: qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); fail_put: - if (nb_clusters > 0) { + if (m->nb_clusters > 0) { QLIST_REMOVE(m, next_in_flight); } return ret; @@ -1015,9 +1049,7 @@ static int discard_single_l2(BlockDriverState *bs, uint64_t offset, uint64_t old_offset; old_offset = be64_to_cpu(l2_table[l2_index + i]); - old_offset &= ~QCOW_OFLAG_COPIED; - - if (old_offset == 0) { + if ((old_offset & L2E_OFFSET_MASK) == 0) { continue; } @@ -1070,3 +1102,75 @@ int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset, return 0; } + +/* + * This zeroes as many clusters of nb_clusters as possible at once (i.e. + * all clusters in the same L2 table) and returns the number of zeroed + * clusters. + */ +static int zero_single_l2(BlockDriverState *bs, uint64_t offset, + unsigned int nb_clusters) +{ + BDRVQcowState *s = bs->opaque; + uint64_t *l2_table; + int l2_index; + int ret; + int i; + + ret = get_cluster_table(bs, offset, &l2_table, &l2_index); + if (ret < 0) { + return ret; + } + + /* Limit nb_clusters to one L2 table */ + nb_clusters = MIN(nb_clusters, s->l2_size - l2_index); + + for (i = 0; i < nb_clusters; i++) { + uint64_t old_offset; + + old_offset = be64_to_cpu(l2_table[l2_index + i]); + + /* Update L2 entries */ + qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table); + if (old_offset & QCOW_OFLAG_COMPRESSED) { + l2_table[l2_index + i] = cpu_to_be64(QCOW_OFLAG_ZERO); + qcow2_free_any_clusters(bs, old_offset, 1); + } else { + l2_table[l2_index + i] |= cpu_to_be64(QCOW_OFLAG_ZERO); + } + } + + ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); + if (ret < 0) { + return ret; + } + + return nb_clusters; +} + +int qcow2_zero_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors) +{ + BDRVQcowState *s = bs->opaque; + unsigned int nb_clusters; + int ret; + + /* The zero flag is only supported by version 3 and newer */ + if (s->qcow_version < 3) { + return -ENOTSUP; + } + + /* Each L2 table is handled by its own loop iteration */ + nb_clusters = size_to_clusters(s, nb_sectors << BDRV_SECTOR_BITS); + + while (nb_clusters > 0) { + ret = zero_single_l2(bs, offset, nb_clusters); + if (ret < 0) { + return ret; + } + + nb_clusters -= ret; + offset += (ret * s->cluster_size); + } + + return 0; +} diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c index f39928a..812c93c 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -167,7 +167,7 @@ static int alloc_refcount_block(BlockDriverState *bs, if (refcount_table_index < s->refcount_table_size) { uint64_t refcount_block_offset = - s->refcount_table[refcount_table_index]; + s->refcount_table[refcount_table_index] & REFT_OFFSET_MASK; /* If it's already there, we're done */ if (refcount_block_offset) { @@ -400,7 +400,7 @@ static int alloc_refcount_block(BlockDriverState *bs, return ret; } - return new_block; + return 0; fail_table: g_free(new_table); @@ -587,6 +587,7 @@ int qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset, { BDRVQcowState *s = bs->opaque; uint64_t cluster_index; + uint64_t old_free_cluster_index; int i, refcount, ret; /* Check how many clusters there are free */ @@ -602,11 +603,16 @@ int qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset, } /* And then allocate them */ + old_free_cluster_index = s->free_cluster_index; + s->free_cluster_index = cluster_index + i; + ret = update_refcount(bs, offset, i << s->cluster_bits, 1); if (ret < 0) { return ret; } + s->free_cluster_index = old_free_cluster_index; + return i; } @@ -673,32 +679,35 @@ void qcow2_free_clusters(BlockDriverState *bs, } /* - * free_any_clusters - * - * free clusters according to its type: compressed or not - * + * Free a cluster using its L2 entry (handles clusters of all types, e.g. + * normal cluster, compressed cluster, etc.) */ - void qcow2_free_any_clusters(BlockDriverState *bs, - uint64_t cluster_offset, int nb_clusters) + uint64_t l2_entry, int nb_clusters) { BDRVQcowState *s = bs->opaque; - /* free the cluster */ - - if (cluster_offset & QCOW_OFLAG_COMPRESSED) { - int nb_csectors; - nb_csectors = ((cluster_offset >> s->csize_shift) & - s->csize_mask) + 1; - qcow2_free_clusters(bs, - (cluster_offset & s->cluster_offset_mask) & ~511, - nb_csectors * 512); - return; + switch (qcow2_get_cluster_type(l2_entry)) { + case QCOW2_CLUSTER_COMPRESSED: + { + int nb_csectors; + nb_csectors = ((l2_entry >> s->csize_shift) & + s->csize_mask) + 1; + qcow2_free_clusters(bs, + (l2_entry & s->cluster_offset_mask) & ~511, + nb_csectors * 512); + } + break; + case QCOW2_CLUSTER_NORMAL: + qcow2_free_clusters(bs, l2_entry & L2E_OFFSET_MASK, + nb_clusters << s->cluster_bits); + break; + case QCOW2_CLUSTER_UNALLOCATED: + case QCOW2_CLUSTER_ZERO: + break; + default: + abort(); } - - qcow2_free_clusters(bs, cluster_offset, nb_clusters << s->cluster_bits); - - return; } @@ -758,7 +767,7 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, l2_offset = l1_table[i]; if (l2_offset) { old_l2_offset = l2_offset; - l2_offset &= ~QCOW_OFLAG_COPIED; + l2_offset &= L1E_OFFSET_MASK; ret = qcow2_cache_get(bs, s->l2_table_cache, l2_offset, (void**) &l2_table); @@ -790,10 +799,11 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, /* compressed clusters are never modified */ refcount = 2; } else { + uint64_t cluster_index = (offset & L2E_OFFSET_MASK) >> s->cluster_bits; if (addend != 0) { - refcount = update_cluster_refcount(bs, offset >> s->cluster_bits, addend); + refcount = update_cluster_refcount(bs, cluster_index, addend); } else { - refcount = get_refcount(bs, offset >> s->cluster_bits); + refcount = get_refcount(bs, cluster_index); } if (refcount < 0) { @@ -931,7 +941,7 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res, int check_copied) { BDRVQcowState *s = bs->opaque; - uint64_t *l2_table, offset; + uint64_t *l2_table, l2_entry; int i, l2_size, nb_csectors, refcount; /* Read L2 table from disk */ @@ -943,54 +953,70 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res, /* Do the actual checks */ for(i = 0; i < s->l2_size; i++) { - offset = be64_to_cpu(l2_table[i]); - if (offset != 0) { - if (offset & QCOW_OFLAG_COMPRESSED) { - /* Compressed clusters don't have QCOW_OFLAG_COPIED */ - if (offset & QCOW_OFLAG_COPIED) { - fprintf(stderr, "ERROR: cluster %" PRId64 ": " - "copied flag must never be set for compressed " - "clusters\n", offset >> s->cluster_bits); - offset &= ~QCOW_OFLAG_COPIED; - res->corruptions++; - } + l2_entry = be64_to_cpu(l2_table[i]); + + switch (qcow2_get_cluster_type(l2_entry)) { + case QCOW2_CLUSTER_COMPRESSED: + /* Compressed clusters don't have QCOW_OFLAG_COPIED */ + if (l2_entry & QCOW_OFLAG_COPIED) { + fprintf(stderr, "ERROR: cluster %" PRId64 ": " + "copied flag must never be set for compressed " + "clusters\n", l2_entry >> s->cluster_bits); + l2_entry &= ~QCOW_OFLAG_COPIED; + res->corruptions++; + } - /* Mark cluster as used */ - nb_csectors = ((offset >> s->csize_shift) & - s->csize_mask) + 1; - offset &= s->cluster_offset_mask; - inc_refcounts(bs, res, refcount_table, refcount_table_size, - offset & ~511, nb_csectors * 512); - } else { - /* QCOW_OFLAG_COPIED must be set iff refcount == 1 */ - if (check_copied) { - uint64_t entry = offset; - offset &= ~QCOW_OFLAG_COPIED; - refcount = get_refcount(bs, offset >> s->cluster_bits); - if (refcount < 0) { - fprintf(stderr, "Can't get refcount for offset %" - PRIx64 ": %s\n", entry, strerror(-refcount)); - goto fail; - } - if ((refcount == 1) != ((entry & QCOW_OFLAG_COPIED) != 0)) { - fprintf(stderr, "ERROR OFLAG_COPIED: offset=%" - PRIx64 " refcount=%d\n", entry, refcount); - res->corruptions++; - } - } + /* Mark cluster as used */ + nb_csectors = ((l2_entry >> s->csize_shift) & + s->csize_mask) + 1; + l2_entry &= s->cluster_offset_mask; + inc_refcounts(bs, res, refcount_table, refcount_table_size, + l2_entry & ~511, nb_csectors * 512); + break; - /* Mark cluster as used */ - offset &= ~QCOW_OFLAG_COPIED; - inc_refcounts(bs, res, refcount_table,refcount_table_size, - offset, s->cluster_size); + case QCOW2_CLUSTER_ZERO: + if ((l2_entry & L2E_OFFSET_MASK) == 0) { + break; + } + /* fall through */ - /* Correct offsets are cluster aligned */ - if (offset & (s->cluster_size - 1)) { - fprintf(stderr, "ERROR offset=%" PRIx64 ": Cluster is not " - "properly aligned; L2 entry corrupted.\n", offset); + case QCOW2_CLUSTER_NORMAL: + { + /* QCOW_OFLAG_COPIED must be set iff refcount == 1 */ + uint64_t offset = l2_entry & L2E_OFFSET_MASK; + + if (check_copied) { + refcount = get_refcount(bs, offset >> s->cluster_bits); + if (refcount < 0) { + fprintf(stderr, "Can't get refcount for offset %" + PRIx64 ": %s\n", l2_entry, strerror(-refcount)); + goto fail; + } + if ((refcount == 1) != ((l2_entry & QCOW_OFLAG_COPIED) != 0)) { + fprintf(stderr, "ERROR OFLAG_COPIED: offset=%" + PRIx64 " refcount=%d\n", l2_entry, refcount); res->corruptions++; } } + + /* Mark cluster as used */ + inc_refcounts(bs, res, refcount_table,refcount_table_size, + offset, s->cluster_size); + + /* Correct offsets are cluster aligned */ + if (offset & (s->cluster_size - 1)) { + fprintf(stderr, "ERROR offset=%" PRIx64 ": Cluster is not " + "properly aligned; L2 entry corrupted.\n", offset); + res->corruptions++; + } + break; + } + + case QCOW2_CLUSTER_UNALLOCATED: + break; + + default: + abort(); } } @@ -1061,7 +1087,7 @@ static int check_refcounts_l1(BlockDriverState *bs, } /* Mark L2 table as used */ - l2_offset &= ~QCOW_OFLAG_COPIED; + l2_offset &= L1E_OFFSET_MASK; inc_refcounts(bs, res, refcount_table, refcount_table_size, l2_offset, s->cluster_size); diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c index 7d3fde5..42f971b 100644 --- a/block/qcow2-snapshot.c +++ b/block/qcow2-snapshot.c @@ -48,6 +48,7 @@ typedef struct QEMU_PACKED QCowSnapshotHeader { typedef struct QEMU_PACKED QCowSnapshotExtraData { uint64_t vm_state_size_large; + uint64_t disk_size; } QCowSnapshotExtraData; void qcow2_free_snapshots(BlockDriverState *bs) @@ -117,6 +118,12 @@ int qcow2_read_snapshots(BlockDriverState *bs) sn->vm_state_size = be64_to_cpu(extra.vm_state_size_large); } + if (extra_data_size >= 16) { + sn->disk_size = be64_to_cpu(extra.disk_size); + } else { + sn->disk_size = bs->total_sectors * BDRV_SECTOR_SIZE; + } + /* Read snapshot ID */ sn->id_str = g_malloc(id_str_size + 1); ret = bdrv_pread(bs->file, offset, sn->id_str, id_str_size); @@ -197,6 +204,7 @@ static int qcow2_write_snapshots(BlockDriverState *bs) memset(&extra, 0, sizeof(extra)); extra.vm_state_size_large = cpu_to_be64(sn->vm_state_size); + extra.disk_size = cpu_to_be64(sn->disk_size); id_str_size = strlen(sn->id_str); name_size = strlen(sn->name); @@ -330,6 +338,7 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) sn->id_str = g_strdup(sn_info->id_str); sn->name = g_strdup(sn_info->name); + sn->disk_size = bs->total_sectors * BDRV_SECTOR_SIZE; sn->vm_state_size = sn_info->vm_state_size; sn->date_sec = sn_info->date_sec; sn->date_nsec = sn_info->date_nsec; @@ -426,6 +435,13 @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id) } sn = &s->snapshots[snapshot_index]; + if (sn->disk_size != bs->total_sectors * BDRV_SECTOR_SIZE) { + error_report("qcow2: Loading snapshots with different disk " + "size is not implemented"); + ret = -ENOTSUP; + goto fail; + } + /* * Make sure that the current L1 table is big enough to contain the whole * L1 table of the snapshot. If the snapshot L1 table is smaller, the diff --git a/block/qcow2.c b/block/qcow2.c index 70d3141..ad46c03 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -54,6 +54,7 @@ typedef struct { } QCowExtension; #define QCOW2_EXT_MAGIC_END 0 #define QCOW2_EXT_MAGIC_BACKING_FORMAT 0xE2792ACA +#define QCOW2_EXT_MAGIC_FEATURE_TABLE 0x6803f857 static int qcow2_probe(const uint8_t *buf, int buf_size, const char *filename) { @@ -61,7 +62,7 @@ static int qcow2_probe(const uint8_t *buf, int buf_size, const char *filename) if (buf_size >= sizeof(QCowHeader) && be32_to_cpu(cow_header->magic) == QCOW_MAGIC && - be32_to_cpu(cow_header->version) >= QCOW_VERSION) + be32_to_cpu(cow_header->version) >= 2) return 100; else return 0; @@ -76,7 +77,7 @@ static int qcow2_probe(const uint8_t *buf, int buf_size, const char *filename) * return 0 upon success, non-0 otherwise */ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, - uint64_t end_offset) + uint64_t end_offset, void **p_feature_table) { BDRVQcowState *s = bs->opaque; QCowExtension ext; @@ -134,6 +135,18 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, #endif break; + case QCOW2_EXT_MAGIC_FEATURE_TABLE: + if (p_feature_table != NULL) { + void* feature_table = g_malloc0(ext.len + 2 * sizeof(Qcow2Feature)); + ret = bdrv_pread(bs->file, offset , feature_table, ext.len); + if (ret < 0) { + return ret; + } + + *p_feature_table = feature_table; + } + break; + default: /* unknown magic - save it in case we need to rewrite the header */ { @@ -169,6 +182,37 @@ static void cleanup_unknown_header_ext(BlockDriverState *bs) } } +static void report_unsupported(BlockDriverState *bs, const char *fmt, ...) +{ + char msg[64]; + va_list ap; + + va_start(ap, fmt); + vsnprintf(msg, sizeof(msg), fmt, ap); + va_end(ap); + + qerror_report(QERR_UNKNOWN_BLOCK_FORMAT_FEATURE, + bs->device_name, "qcow2", msg); +} + +static void report_unsupported_feature(BlockDriverState *bs, + Qcow2Feature *table, uint64_t mask) +{ + while (table && table->name[0] != '\0') { + if (table->type == QCOW2_FEAT_TYPE_INCOMPATIBLE) { + if (mask & (1 << table->bit)) { + report_unsupported(bs, "%.46s",table->name); + mask &= ~(1 << table->bit); + } + } + table++; + } + + if (mask) { + report_unsupported(bs, "Unknown incompatible feature: %" PRIx64, mask); + } +} + static int qcow2_open(BlockDriverState *bs, int flags) { BDRVQcowState *s = bs->opaque; @@ -199,14 +243,73 @@ static int qcow2_open(BlockDriverState *bs, int flags) ret = -EINVAL; goto fail; } - if (header.version != QCOW_VERSION) { - char version[64]; - snprintf(version, sizeof(version), "QCOW version %d", header.version); - qerror_report(QERR_UNKNOWN_BLOCK_FORMAT_FEATURE, - bs->device_name, "qcow2", version); + if (header.version < 2 || header.version > 3) { + report_unsupported(bs, "QCOW version %d", header.version); + ret = -ENOTSUP; + goto fail; + } + + s->qcow_version = header.version; + + /* Initialise version 3 header fields */ + if (header.version == 2) { + header.incompatible_features = 0; + header.compatible_features = 0; + header.autoclear_features = 0; + header.refcount_order = 4; + header.header_length = 72; + } else { + be64_to_cpus(&header.incompatible_features); + be64_to_cpus(&header.compatible_features); + be64_to_cpus(&header.autoclear_features); + be32_to_cpus(&header.refcount_order); + be32_to_cpus(&header.header_length); + } + + if (header.header_length > sizeof(header)) { + s->unknown_header_fields_size = header.header_length - sizeof(header); + s->unknown_header_fields = g_malloc(s->unknown_header_fields_size); + ret = bdrv_pread(bs->file, sizeof(header), s->unknown_header_fields, + s->unknown_header_fields_size); + if (ret < 0) { + goto fail; + } + } + + if (header.backing_file_offset) { + ext_end = header.backing_file_offset; + } else { + ext_end = 1 << header.cluster_bits; + } + + /* Handle feature bits */ + s->incompatible_features = header.incompatible_features; + s->compatible_features = header.compatible_features; + s->autoclear_features = header.autoclear_features; + + if (s->incompatible_features != 0) { + void *feature_table = NULL; + qcow2_read_extensions(bs, header.header_length, ext_end, + &feature_table); + report_unsupported_feature(bs, feature_table, + s->incompatible_features); + ret = -ENOTSUP; + goto fail; + } + + if (!bs->read_only && s->autoclear_features != 0) { + s->autoclear_features = 0; + qcow2_update_header(bs); + } + + /* Check support for various header values */ + if (header.refcount_order != 4) { + report_unsupported(bs, "%d bit reference counts", + 1 << header.refcount_order); ret = -ENOTSUP; goto fail; } + if (header.cluster_bits < MIN_CLUSTER_BITS || header.cluster_bits > MAX_CLUSTER_BITS) { ret = -EINVAL; @@ -280,12 +383,7 @@ static int qcow2_open(BlockDriverState *bs, int flags) QLIST_INIT(&s->cluster_allocs); /* read qcow2 extensions */ - if (header.backing_file_offset) { - ext_end = header.backing_file_offset; - } else { - ext_end = s->cluster_size; - } - if (qcow2_read_extensions(bs, sizeof(header), ext_end)) { + if (qcow2_read_extensions(bs, header.header_length, ext_end, NULL)) { ret = -EINVAL; goto fail; } @@ -321,6 +419,7 @@ static int qcow2_open(BlockDriverState *bs, int flags) return ret; fail: + g_free(s->unknown_header_fields); cleanup_unknown_header_ext(bs); qcow2_free_snapshots(bs); qcow2_refcount_close(bs); @@ -449,7 +548,8 @@ static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num, qemu_iovec_copy(&hd_qiov, qiov, bytes_done, cur_nr_sectors * 512); - if (!cluster_offset) { + switch (ret) { + case QCOW2_CLUSTER_UNALLOCATED: if (bs->backing_hd) { /* read from the base image */ @@ -469,7 +569,17 @@ static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num, /* Note: in this case, no need to wait */ qemu_iovec_memset(&hd_qiov, 0, 512 * cur_nr_sectors); } - } else if (cluster_offset & QCOW_OFLAG_COMPRESSED) { + break; + + case QCOW2_CLUSTER_ZERO: + if (s->qcow_version < 3) { + ret = -EIO; + goto fail; + } + qemu_iovec_memset(&hd_qiov, 0, 512 * cur_nr_sectors); + break; + + case QCOW2_CLUSTER_COMPRESSED: /* add AIO support for compressed blocks ? */ ret = qcow2_decompress_cluster(bs, cluster_offset); if (ret < 0) { @@ -479,7 +589,9 @@ static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num, qemu_iovec_from_buffer(&hd_qiov, s->cluster_cache + index_in_cluster * 512, 512 * cur_nr_sectors); - } else { + break; + + case QCOW2_CLUSTER_NORMAL: if ((cluster_offset & 511) != 0) { ret = -EIO; goto fail; @@ -520,6 +632,12 @@ static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num, qemu_iovec_from_buffer(&hd_qiov, cluster_data, 512 * cur_nr_sectors); } + break; + + default: + g_assert_not_reached(); + ret = -EIO; + goto fail; } remaining_sectors -= cur_nr_sectors; @@ -671,7 +789,9 @@ static void qcow2_close(BlockDriverState *bs) qcow2_cache_destroy(bs, s->l2_table_cache); qcow2_cache_destroy(bs, s->refcount_block_cache); + g_free(s->unknown_header_fields); cleanup_unknown_header_ext(bs); + g_free(s->cluster_cache); qemu_vfree(s->cluster_data); qcow2_refcount_close(bs); @@ -745,10 +865,10 @@ int qcow2_update_header(BlockDriverState *bs) int ret; uint64_t total_size; uint32_t refcount_table_clusters; + size_t header_length; Qcow2UnknownHeaderExtension *uext; buf = qemu_blockalign(bs, buflen); - memset(buf, 0, s->cluster_size); /* Header structure */ header = (QCowHeader*) buf; @@ -758,12 +878,14 @@ int qcow2_update_header(BlockDriverState *bs) goto fail; } + header_length = sizeof(*header) + s->unknown_header_fields_size; total_size = bs->total_sectors * BDRV_SECTOR_SIZE; refcount_table_clusters = s->refcount_table_size >> (s->cluster_bits - 3); *header = (QCowHeader) { + /* Version 2 fields */ .magic = cpu_to_be32(QCOW_MAGIC), - .version = cpu_to_be32(QCOW_VERSION), + .version = cpu_to_be32(s->qcow_version), .backing_file_offset = 0, .backing_file_size = 0, .cluster_bits = cpu_to_be32(s->cluster_bits), @@ -775,10 +897,42 @@ int qcow2_update_header(BlockDriverState *bs) .refcount_table_clusters = cpu_to_be32(refcount_table_clusters), .nb_snapshots = cpu_to_be32(s->nb_snapshots), .snapshots_offset = cpu_to_be64(s->snapshots_offset), + + /* Version 3 fields */ + .incompatible_features = cpu_to_be64(s->incompatible_features), + .compatible_features = cpu_to_be64(s->compatible_features), + .autoclear_features = cpu_to_be64(s->autoclear_features), + .refcount_order = cpu_to_be32(3 + REFCOUNT_SHIFT), + .header_length = cpu_to_be32(header_length), }; - buf += sizeof(*header); - buflen -= sizeof(*header); + /* For older versions, write a shorter header */ + switch (s->qcow_version) { + case 2: + ret = offsetof(QCowHeader, incompatible_features); + break; + case 3: + ret = sizeof(*header); + break; + default: + return -EINVAL; + } + + buf += ret; + buflen -= ret; + memset(buf, 0, buflen); + + /* Preserve any unknown field in the header */ + if (s->unknown_header_fields_size) { + if (buflen < s->unknown_header_fields_size) { + ret = -ENOSPC; + goto fail; + } + + memcpy(buf, s->unknown_header_fields, s->unknown_header_fields_size); + buf += s->unknown_header_fields_size; + buflen -= s->unknown_header_fields_size; + } /* Backing file format header extension */ if (*bs->backing_format) { @@ -793,6 +947,19 @@ int qcow2_update_header(BlockDriverState *bs) buflen -= ret; } + /* Feature table */ + Qcow2Feature features[] = { + /* no feature defined yet */ + }; + + ret = header_ext_add(buf, QCOW2_EXT_MAGIC_FEATURE_TABLE, + features, sizeof(features), buflen); + if (ret < 0) { + goto fail; + } + buf += ret; + buflen -= ret; + /* Keep unknown header extensions */ QLIST_FOREACH(uext, &s->unknown_header_ext, next) { ret = header_ext_add(buf, uext->magic, uext->data, uext->len, buflen); @@ -910,7 +1077,7 @@ static int preallocate(BlockDriverState *bs) static int qcow2_create2(const char *filename, int64_t total_size, const char *backing_file, const char *backing_format, int flags, size_t cluster_size, int prealloc, - QEMUOptionParameter *options) + QEMUOptionParameter *options, int version) { /* Calculate cluster_bits */ int cluster_bits; @@ -954,13 +1121,15 @@ static int qcow2_create2(const char *filename, int64_t total_size, /* Write the header */ memset(&header, 0, sizeof(header)); header.magic = cpu_to_be32(QCOW_MAGIC); - header.version = cpu_to_be32(QCOW_VERSION); + header.version = cpu_to_be32(version); header.cluster_bits = cpu_to_be32(cluster_bits); header.size = cpu_to_be64(0); header.l1_table_offset = cpu_to_be64(0); header.l1_size = cpu_to_be32(0); header.refcount_table_offset = cpu_to_be64(cluster_size); header.refcount_table_clusters = cpu_to_be32(1); + header.refcount_order = cpu_to_be32(3 + REFCOUNT_SHIFT); + header.header_length = cpu_to_be32(sizeof(header)); if (flags & BLOCK_FLAG_ENCRYPT) { header.crypt_method = cpu_to_be32(QCOW_CRYPT_AES); @@ -1042,6 +1211,7 @@ static int qcow2_create(const char *filename, QEMUOptionParameter *options) int flags = 0; size_t cluster_size = DEFAULT_CLUSTER_SIZE; int prealloc = 0; + int version = 2; /* Read out options */ while (options && options->name) { @@ -1067,6 +1237,16 @@ static int qcow2_create(const char *filename, QEMUOptionParameter *options) options->value.s); return -EINVAL; } + } else if (!strcmp(options->name, BLOCK_OPT_COMPAT_LEVEL)) { + if (!options->value.s || !strcmp(options->value.s, "0.10")) { + version = 2; + } else if (!strcmp(options->value.s, "1.1")) { + version = 3; + } else { + fprintf(stderr, "Invalid compatibility level: '%s'\n", + options->value.s); + return -EINVAL; + } } options++; } @@ -1078,7 +1258,7 @@ static int qcow2_create(const char *filename, QEMUOptionParameter *options) } return qcow2_create2(filename, sectors, backing_file, backing_fmt, flags, - cluster_size, prealloc, options); + cluster_size, prealloc, options, version); } static int qcow2_make_empty(BlockDriverState *bs) @@ -1101,6 +1281,26 @@ static int qcow2_make_empty(BlockDriverState *bs) return 0; } +static coroutine_fn int qcow2_co_write_zeroes(BlockDriverState *bs, + int64_t sector_num, int nb_sectors) +{ + int ret; + BDRVQcowState *s = bs->opaque; + + /* Emulate misaligned zero writes */ + if (sector_num % s->cluster_sectors || nb_sectors % s->cluster_sectors) { + return -ENOTSUP; + } + + /* Whatever is left can use real zero clusters */ + qemu_co_mutex_lock(&s->lock); + ret = qcow2_zero_clusters(bs, sector_num << BDRV_SECTOR_BITS, + nb_sectors); + qemu_co_mutex_unlock(&s->lock); + + return ret; +} + static coroutine_fn int qcow2_co_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors) { @@ -1330,6 +1530,11 @@ static QEMUOptionParameter qcow2_create_options[] = { .help = "Virtual disk size" }, { + .name = BLOCK_OPT_COMPAT_LEVEL, + .type = OPT_STRING, + .help = "Compatibility level (0.10 or 1.1)" + }, + { .name = BLOCK_OPT_BACKING_FILE, .type = OPT_STRING, .help = "File name of a base image" @@ -1373,6 +1578,7 @@ static BlockDriver bdrv_qcow2 = { .bdrv_co_writev = qcow2_co_writev, .bdrv_co_flush_to_os = qcow2_co_flush_to_os, + .bdrv_co_write_zeroes = qcow2_co_write_zeroes, .bdrv_co_discard = qcow2_co_discard, .bdrv_truncate = qcow2_truncate, .bdrv_write_compressed = qcow2_write_compressed, diff --git a/block/qcow2.h b/block/qcow2.h index e4ac366..93567f6 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -33,7 +33,6 @@ //#define DEBUG_EXT #define QCOW_MAGIC (('Q' << 24) | ('F' << 16) | ('I' << 8) | 0xfb) -#define QCOW_VERSION 2 #define QCOW_CRYPT_NONE 0 #define QCOW_CRYPT_AES 1 @@ -44,6 +43,8 @@ #define QCOW_OFLAG_COPIED (1LL << 63) /* indicate that the cluster is compressed (they never have the copied flag) */ #define QCOW_OFLAG_COMPRESSED (1LL << 62) +/* The cluster reads as all zeros */ +#define QCOW_OFLAG_ZERO (1LL << 0) #define REFCOUNT_SHIFT 1 /* refcount size is 2 bytes */ @@ -71,6 +72,14 @@ typedef struct QCowHeader { uint32_t refcount_table_clusters; uint32_t nb_snapshots; uint64_t snapshots_offset; + + /* The following fields are only valid for version >= 3 */ + uint64_t incompatible_features; + uint64_t compatible_features; + uint64_t autoclear_features; + + uint32_t refcount_order; + uint32_t header_length; } QCowHeader; typedef struct QCowSnapshot { @@ -78,6 +87,7 @@ typedef struct QCowSnapshot { uint32_t l1_size; char *id_str; char *name; + uint64_t disk_size; uint64_t vm_state_size; uint32_t date_sec; uint32_t date_nsec; @@ -94,6 +104,18 @@ typedef struct Qcow2UnknownHeaderExtension { uint8_t data[]; } Qcow2UnknownHeaderExtension; +enum { + QCOW2_FEAT_TYPE_INCOMPATIBLE = 0, + QCOW2_FEAT_TYPE_COMPATIBLE = 1, + QCOW2_FEAT_TYPE_AUTOCLEAR = 2, +}; + +typedef struct Qcow2Feature { + uint8_t type; + uint8_t bit; + char name[46]; +} QEMU_PACKED Qcow2Feature; + typedef struct BDRVQcowState { int cluster_bits; int cluster_size; @@ -134,6 +156,14 @@ typedef struct BDRVQcowState { QCowSnapshot *snapshots; int flags; + int qcow_version; + + uint64_t incompatible_features; + uint64_t compatible_features; + uint64_t autoclear_features; + + size_t unknown_header_fields_size; + void* unknown_header_fields; QLIST_HEAD(, Qcow2UnknownHeaderExtension) unknown_header_ext; } BDRVQcowState; @@ -164,6 +194,19 @@ typedef struct QCowL2Meta QLIST_ENTRY(QCowL2Meta) next_in_flight; } QCowL2Meta; +enum { + QCOW2_CLUSTER_UNALLOCATED, + QCOW2_CLUSTER_NORMAL, + QCOW2_CLUSTER_COMPRESSED, + QCOW2_CLUSTER_ZERO +}; + +#define L1E_OFFSET_MASK 0x00ffffffffffff00ULL +#define L2E_OFFSET_MASK 0x00ffffffffffff00ULL +#define L2E_COMPRESSED_OFFSET_SIZE_MASK 0x3fffffffffffffffULL + +#define REFT_OFFSET_MASK 0xffffffffffffff00ULL + static inline int size_to_clusters(BDRVQcowState *s, int64_t size) { return (size + (s->cluster_size - 1)) >> s->cluster_bits; @@ -181,6 +224,19 @@ static inline int64_t align_offset(int64_t offset, int n) return offset; } +static inline int qcow2_get_cluster_type(uint64_t l2_entry) +{ + if (l2_entry & QCOW_OFLAG_COMPRESSED) { + return QCOW2_CLUSTER_COMPRESSED; + } else if (l2_entry & QCOW_OFLAG_ZERO) { + return QCOW2_CLUSTER_ZERO; + } else if (!(l2_entry & L2E_OFFSET_MASK)) { + return QCOW2_CLUSTER_UNALLOCATED; + } else { + return QCOW2_CLUSTER_NORMAL; + } +} + // FIXME Need qcow2_ prefix to global functions @@ -227,6 +283,7 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m); int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors); +int qcow2_zero_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors); /* qcow2-snapshot.c functions */ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info); diff --git a/block/rbd.c b/block/rbd.c index 46a8579..6cd8448 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -504,7 +504,7 @@ static int qemu_rbd_open(BlockDriverState *bs, const char *filename, int flags) fcntl(s->fds[0], F_SETFL, O_NONBLOCK); fcntl(s->fds[1], F_SETFL, O_NONBLOCK); qemu_aio_set_fd_handler(s->fds[RBD_FD_READ], qemu_rbd_aio_event_reader, - NULL, qemu_rbd_aio_flush_cb, NULL, s); + NULL, qemu_rbd_aio_flush_cb, s); return 0; @@ -525,8 +525,7 @@ static void qemu_rbd_close(BlockDriverState *bs) close(s->fds[0]); close(s->fds[1]); - qemu_aio_set_fd_handler(s->fds[RBD_FD_READ], NULL , NULL, NULL, NULL, - NULL); + qemu_aio_set_fd_handler(s->fds[RBD_FD_READ], NULL, NULL, NULL, NULL); rbd_close(s->image); rados_ioctx_destroy(s->io_ctx); diff --git a/block/sheepdog.c b/block/sheepdog.c index 3eaf625..0ed6b19 100644 --- a/block/sheepdog.c +++ b/block/sheepdog.c @@ -799,8 +799,7 @@ static int get_sheep_fd(BDRVSheepdogState *s) return -1; } - qemu_aio_set_fd_handler(fd, co_read_response, NULL, aio_flush_request, - NULL, s); + qemu_aio_set_fd_handler(fd, co_read_response, NULL, aio_flush_request, s); return fd; } @@ -973,7 +972,7 @@ static int coroutine_fn add_aio_request(BDRVSheepdogState *s, AIOReq *aio_req, qemu_co_mutex_lock(&s->lock); s->co_send = qemu_coroutine_self(); qemu_aio_set_fd_handler(s->fd, co_read_response, co_write_request, - aio_flush_request, NULL, s); + aio_flush_request, s); socket_set_cork(s->fd, 1); /* send a header */ @@ -995,7 +994,7 @@ static int coroutine_fn add_aio_request(BDRVSheepdogState *s, AIOReq *aio_req, socket_set_cork(s->fd, 0); qemu_aio_set_fd_handler(s->fd, co_read_response, NULL, - aio_flush_request, NULL, s); + aio_flush_request, s); qemu_co_mutex_unlock(&s->lock); return 0; @@ -1135,7 +1134,7 @@ static int sd_open(BlockDriverState *bs, const char *filename, int flags) g_free(buf); return 0; out: - qemu_aio_set_fd_handler(s->fd, NULL, NULL, NULL, NULL, NULL); + qemu_aio_set_fd_handler(s->fd, NULL, NULL, NULL, NULL); if (s->fd >= 0) { closesocket(s->fd); } @@ -1349,7 +1348,7 @@ static void sd_close(BlockDriverState *bs) error_report("%s, %s", sd_strerror(rsp->result), s->name); } - qemu_aio_set_fd_handler(s->fd, NULL, NULL, NULL, NULL, NULL); + qemu_aio_set_fd_handler(s->fd, NULL, NULL, NULL, NULL); closesocket(s->fd); if (s->cache_enabled) { closesocket(s->flush_fd); diff --git a/block_int.h b/block_int.h index 0e5a032..0acb49f 100644 --- a/block_int.h +++ b/block_int.h @@ -50,6 +50,7 @@ #define BLOCK_OPT_TABLE_SIZE "table_size" #define BLOCK_OPT_PREALLOC "preallocation" #define BLOCK_OPT_SUBFMT "subformat" +#define BLOCK_OPT_COMPAT_LEVEL "compat" typedef struct BdrvTrackedRequest BdrvTrackedRequest; @@ -25,6 +25,7 @@ #include "cmd.h" #include "qemu-aio.h" +#include "main-loop.h" #define _(x) x /* not gettext support yet */ @@ -146,7 +147,7 @@ static void prep_fetchline(void *opaque) { int *fetchable = opaque; - qemu_aio_set_fd_handler(STDIN_FILENO, NULL, NULL, NULL, NULL, NULL); + qemu_set_fd_handler(STDIN_FILENO, NULL, NULL, NULL); *fetchable= 1; } @@ -193,12 +194,11 @@ void command_loop(void) if (!prompted) { printf("%s", get_prompt()); fflush(stdout); - qemu_aio_set_fd_handler(STDIN_FILENO, prep_fetchline, NULL, NULL, - NULL, &fetchable); + qemu_set_fd_handler(STDIN_FILENO, prep_fetchline, NULL, &fetchable); prompted = 1; } - qemu_aio_wait(); + main_loop_wait(false); if (!fetchable) { continue; @@ -221,7 +221,7 @@ void command_loop(void) prompted = 0; fetchable = 0; } - qemu_aio_set_fd_handler(STDIN_FILENO, NULL, NULL, NULL, NULL, NULL); + qemu_set_fd_handler(STDIN_FILENO, NULL, NULL, NULL); } /* from libxcmd/input.c */ @@ -1097,7 +1097,7 @@ echo " --disable-docs disable documentation build" echo " --disable-vhost-net disable vhost-net acceleration support" echo " --enable-vhost-net enable vhost-net acceleration support" echo " --enable-trace-backend=B Set trace backend" -echo " Available backends:" $("$source_path"/scripts/tracetool --list-backends) +echo " Available backends:" $($python "$source_path"/scripts/tracetool.py --list-backends) echo " --with-trace-file=NAME Full PATH,NAME of file to store traces" echo " Default:trace-<pid>" echo " --disable-spice disable spice" @@ -1398,6 +1398,31 @@ int main(void) { xc_hvm_set_mem_type(0, 0, HVMMEM_ram_ro, 0, 0); xc_gnttab_open(NULL, 0); xc_domain_add_to_physmap(0, 0, XENMAPSPACE_gmfn, 0, 0); + xc_hvm_inject_msi(xc, 0, 0xf0000000, 0x00000000); + return 0; +} +EOF + compile_prog "" "$xen_libs" + ) ; then + xen_ctrl_version=420 + xen=yes + + elif ( + cat > $TMPC <<EOF +#include <xenctrl.h> +#include <xs.h> +#include <stdint.h> +#include <xen/hvm/hvm_info_table.h> +#if !defined(HVM_MAX_VCPUS) +# error HVM_MAX_VCPUS not defined +#endif +int main(void) { + xc_interface *xc; + xs_daemon_open(); + xc = xc_interface_open(0, 0, 0); + xc_hvm_set_mem_type(0, 0, HVMMEM_ram_ro, 0, 0); + xc_gnttab_open(NULL, 0); + xc_domain_add_to_physmap(0, 0, XENMAPSPACE_gmfn, 0, 0); return 0; } EOF @@ -2670,7 +2695,7 @@ fi ########################################## # check if trace backend exists -sh "$source_path/scripts/tracetool" "--$trace_backend" --check-backend > /dev/null 2> /dev/null +$python "$source_path/scripts/tracetool.py" "--backend=$trace_backend" --check-backend > /dev/null 2> /dev/null if test "$?" -ne 0 ; then echo echo "Error: invalid trace backend" diff --git a/docs/specs/qcow2.txt b/docs/specs/qcow2.txt index b6adcad..65e6325 100644 --- a/docs/specs/qcow2.txt +++ b/docs/specs/qcow2.txt @@ -18,7 +18,7 @@ The first cluster of a qcow2 image contains the file header: QCOW magic string ("QFI\xfb") 4 - 7: version - Version number (only valid value is 2) + Version number (valid values are 2 and 3) 8 - 15: backing_file_offset Offset into the image file at which the backing file name @@ -67,12 +67,45 @@ The first cluster of a qcow2 image contains the file header: Offset into the image file at which the snapshot table starts. Must be aligned to a cluster boundary. +If the version is 3 or higher, the header has the following additional fields. +For version 2, the values are assumed to be zero, unless specified otherwise +in the description of a field. + + 72 - 79: incompatible_features + Bitmask of incompatible features. An implementation must + fail to open an image if an unknown bit is set. + + Bits 0-63: Reserved (set to 0) + + 80 - 87: compatible_features + Bitmask of compatible features. An implementation can + safely ignore any unknown bits that are set. + + Bits 0-63: Reserved (set to 0) + + 88 - 95: autoclear_features + Bitmask of auto-clear features. An implementation may only + write to an image with unknown auto-clear features if it + clears the respective bits from this field first. + + Bits 0-63: Reserved (set to 0) + + 96 - 99: refcount_order + Describes the width of a reference count block entry (width + in bits = 1 << refcount_order). For version 2 images, the + order is always assumed to be 4 (i.e. the width is 16 bits). + + 100 - 103: header_length + Length of the header structure in bytes. For version 2 + images, the length is always assumed to be 72 bytes. + Directly after the image header, optional sections called header extensions can be stored. Each extension has a structure like the following: Byte 0 - 3: Header extension type: 0x00000000 - End of the header extension area 0xE2792ACA - Backing file format name + 0x6803f857 - Feature name table other - Unknown header extension, can be safely ignored @@ -83,9 +116,37 @@ be stored. Each extension has a structure like the following: n - m: Padding to round up the header extension size to the next multiple of 8. +Unless stated otherwise, each header extension type shall appear at most once +in the same image. + The remaining space between the end of the header extension area and the end of -the first cluster can be used for other data. Usually, the backing file name is -stored there. +the first cluster can be used for the backing file name. It is not allowed to +store other data here, so that an implementation can safely modify the header +and add extensions without harming data of compatible features that it +doesn't support. Compatible features that need space for additional data can +use a header extension. + + +== Feature name table == + +The feature name table is an optional header extension that contains the name +for features used by the image. It can be used by applications that don't know +the respective feature (e.g. because the feature was introduced only later) to +display a useful error message. + +The number of entries in the feature name table is determined by the length of +the header extension data. Each entry look like this: + + Byte 0: Type of feature (select feature bitmap) + 0: Incompatible feature + 1: Compatible feature + 2: Autoclear feature + + 1: Bit number within the selected feature bitmap (valid + values: 0-63) + + 2 - 47: Feature name (padded with zeros, but not necessarily null + terminated if it has full length) == Host cluster management == @@ -126,9 +187,11 @@ Refcount table entry: been allocated. All refcounts managed by this refcount block are 0. -Refcount block entry: +Refcount block entry (x = refcount_bits - 1): - Bit 0 - 15: Reference count of the cluster + Bit 0 - x: Reference count of the cluster. If refcount_bits implies a + sub-byte width, note that bit 0 means the least significant + bit in this context. == Cluster mapping == @@ -168,9 +231,29 @@ L1 table entry: refcount is exactly one. This information is only accurate in the active L1 table. -L2 table entry (for normal clusters): +L2 table entry: - Bit 0 - 8: Reserved (set to 0) + Bit 0 - 61: Cluster descriptor + + 62: 0 for standard clusters + 1 for compressed clusters + + 63: 0 for a cluster that is unused or requires COW, 1 if its + refcount is exactly one. This information is only accurate + in L2 tables that are reachable from the the active L1 + table. + +Standard Cluster Descriptor: + + Bit 0: If set to 1, the cluster reads as all zeros. The host + cluster offset can be used to describe a preallocation, + but it won't be used for reading data from this cluster, + nor is data read from the backing file if the cluster is + unallocated. + + With version 2, this is always 0. + + 1 - 8: Reserved (set to 0) 9 - 55: Bits 9-55 of host cluster offset. Must be aligned to a cluster boundary. If the offset is 0, the cluster is @@ -178,30 +261,18 @@ L2 table entry (for normal clusters): 56 - 61: Reserved (set to 0) - 62: 0 (this cluster is not compressed) - 63: 0 for a cluster that is unused or requires COW, 1 if its - refcount is exactly one. This information is only accurate - in L2 tables that are reachable from the the active L1 - table. - -L2 table entry (for compressed clusters; x = 62 - (cluster_size - 8)): +Compressed Clusters Descriptor (x = 62 - (cluster_size - 8)): Bit 0 - x: Host cluster offset. This is usually _not_ aligned to a cluster boundary! x+1 - 61: Compressed size of the images in sectors of 512 bytes - 62: 1 (this cluster is compressed using zlib) - - 63: 0 for a cluster that is unused or requires COW, 1 if its - refcount is exactly one. This information is only accurate - in L2 tables that are reachable from the the active L1 - table. - If a cluster is unallocated, read requests shall read the data from the backing -file. If there is no backing file or the backing file is smaller than the image, -they shall read zeros for all parts that are not covered by the backing file. +file (except if bit 0 in the Standard Cluster Descriptor is set). If there is +no backing file or the backing file is smaller than the image, they shall read +zeros for all parts that are not covered by the backing file. == Snapshots == @@ -261,6 +332,11 @@ Snapshot table entry: state is saved. If this field is present, the 32-bit value in bytes 32-35 is ignored. + Byte 48 - 55: Virtual disk size of the snapshot in bytes + + Version 3 images must include extra data at least up to + byte 55. + variable: Unique ID string for the snapshot (not null terminated) variable: Name of the snapshot (not null terminated) @@ -93,7 +93,7 @@ QDict *error_get_data(Error *err) void error_set_field(Error *err, const char *field, const char *value) { QDict *dict = qdict_get_qdict(err->obj, "data"); - return qdict_put(dict, field, qstring_from_str(value)); + qdict_put(dict, field, qstring_from_str(value)); } void error_free(Error *err) diff --git a/hmp-commands.hx b/hmp-commands.hx index a6f5a84..461fa59 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -92,8 +92,8 @@ ETEXI }, STEXI -@item block_job_set_stream -@findex block_job_set_stream +@item block_job_set_speed +@findex block_job_set_speed Set maximum speed for a background block operation. ETEXI @@ -481,7 +481,7 @@ process_tx_desc(E1000State *s, struct e1000_tx_desc *dp) } while (split_size -= bytes); } else if (!tp->tse && tp->cptse) { // context descriptor TSE is not set, while data descriptor TSE is set - DBGOUT(TXERR, "TCP segmentaion Error\n"); + DBGOUT(TXERR, "TCP segmentation error\n"); } else { split_size = MIN(sizeof(tp->data) - tp->size, split_size); pci_dma_read(&s->dev, addr, tp->data + tp->size, split_size); diff --git a/hw/ide/core.c b/hw/ide/core.c index 35723fd..a5216a6 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -471,40 +471,68 @@ static void ide_rw_error(IDEState *s) { ide_set_irq(s->bus); } +static void ide_sector_read_cb(void *opaque, int ret) +{ + IDEState *s = opaque; + int n; + + s->pio_aiocb = NULL; + s->status &= ~BUSY_STAT; + + bdrv_acct_done(s->bs, &s->acct); + if (ret != 0) { + if (ide_handle_rw_error(s, -ret, BM_STATUS_PIO_RETRY | + BM_STATUS_RETRY_READ)) { + return; + } + } + + n = s->nsector; + if (n > s->req_nb_sectors) { + n = s->req_nb_sectors; + } + + /* Allow the guest to read the io_buffer */ + ide_transfer_start(s, s->io_buffer, n * BDRV_SECTOR_SIZE, ide_sector_read); + + ide_set_irq(s->bus); + + ide_set_sector(s, ide_get_sector(s) + n); + s->nsector -= n; +} + void ide_sector_read(IDEState *s) { int64_t sector_num; - int ret, n; + int n; s->status = READY_STAT | SEEK_STAT; s->error = 0; /* not needed by IDE spec, but needed by Windows */ sector_num = ide_get_sector(s); n = s->nsector; + if (n == 0) { - /* no more sector to read from disk */ ide_transfer_stop(s); - } else { + return; + } + + s->status |= BUSY_STAT; + + if (n > s->req_nb_sectors) { + n = s->req_nb_sectors; + } + #if defined(DEBUG_IDE) - printf("read sector=%" PRId64 "\n", sector_num); + printf("sector=%" PRId64 "\n", sector_num); #endif - if (n > s->req_nb_sectors) - n = s->req_nb_sectors; - bdrv_acct_start(s->bs, &s->acct, n * BDRV_SECTOR_SIZE, BDRV_ACCT_READ); - ret = bdrv_read(s->bs, sector_num, s->io_buffer, n); - bdrv_acct_done(s->bs, &s->acct); - if (ret != 0) { - if (ide_handle_rw_error(s, -ret, - BM_STATUS_PIO_RETRY | BM_STATUS_RETRY_READ)) - { - return; - } - } - ide_transfer_start(s, s->io_buffer, 512 * n, ide_sector_read); - ide_set_irq(s->bus); - ide_set_sector(s, sector_num + n); - s->nsector -= n; - } + s->iov.iov_base = s->io_buffer; + s->iov.iov_len = n * BDRV_SECTOR_SIZE; + qemu_iovec_init_external(&s->qiov, &s->iov, 1); + + bdrv_acct_start(s->bs, &s->acct, n * BDRV_SECTOR_SIZE, BDRV_ACCT_READ); + s->pio_aiocb = bdrv_aio_readv(s->bs, sector_num, &s->qiov, n, + ide_sector_read_cb, s); } static void dma_buf_commit(IDEState *s) @@ -660,40 +688,39 @@ static void ide_sector_write_timer_cb(void *opaque) ide_set_irq(s->bus); } -void ide_sector_write(IDEState *s) +static void ide_sector_write_cb(void *opaque, int ret) { - int64_t sector_num; - int ret, n, n1; - - s->status = READY_STAT | SEEK_STAT; - sector_num = ide_get_sector(s); -#if defined(DEBUG_IDE) - printf("write sector=%" PRId64 "\n", sector_num); -#endif - n = s->nsector; - if (n > s->req_nb_sectors) - n = s->req_nb_sectors; + IDEState *s = opaque; + int n; - bdrv_acct_start(s->bs, &s->acct, n * BDRV_SECTOR_SIZE, BDRV_ACCT_READ); - ret = bdrv_write(s->bs, sector_num, s->io_buffer, n); bdrv_acct_done(s->bs, &s->acct); + s->pio_aiocb = NULL; + s->status &= ~BUSY_STAT; + if (ret != 0) { - if (ide_handle_rw_error(s, -ret, BM_STATUS_PIO_RETRY)) + if (ide_handle_rw_error(s, -ret, BM_STATUS_PIO_RETRY)) { return; + } } + n = s->nsector; + if (n > s->req_nb_sectors) { + n = s->req_nb_sectors; + } s->nsector -= n; if (s->nsector == 0) { /* no more sectors to write */ ide_transfer_stop(s); } else { - n1 = s->nsector; - if (n1 > s->req_nb_sectors) + int n1 = s->nsector; + if (n1 > s->req_nb_sectors) { n1 = s->req_nb_sectors; - ide_transfer_start(s, s->io_buffer, 512 * n1, ide_sector_write); + } + ide_transfer_start(s, s->io_buffer, n1 * BDRV_SECTOR_SIZE, + ide_sector_write); } - ide_set_sector(s, sector_num + n); + ide_set_sector(s, ide_get_sector(s) + n); if (win2k_install_hack && ((++s->irq_count % 16) == 0)) { /* It seems there is a bug in the Windows 2000 installer HDD @@ -709,6 +736,30 @@ void ide_sector_write(IDEState *s) } } +void ide_sector_write(IDEState *s) +{ + int64_t sector_num; + int n; + + s->status = READY_STAT | SEEK_STAT | BUSY_STAT; + sector_num = ide_get_sector(s); +#if defined(DEBUG_IDE) + printf("sector=%" PRId64 "\n", sector_num); +#endif + n = s->nsector; + if (n > s->req_nb_sectors) { + n = s->req_nb_sectors; + } + + s->iov.iov_base = s->io_buffer; + s->iov.iov_len = n * BDRV_SECTOR_SIZE; + qemu_iovec_init_external(&s->qiov, &s->iov, 1); + + bdrv_acct_start(s->bs, &s->acct, n * BDRV_SECTOR_SIZE, BDRV_ACCT_READ); + s->pio_aiocb = bdrv_aio_writev(s->bs, sector_num, &s->qiov, n, + ide_sector_write_cb, s); +} + static void ide_flush_cb(void *opaque, int ret) { IDEState *s = opaque; @@ -1765,6 +1816,12 @@ static void ide_reset(IDEState *s) #ifdef DEBUG_IDE printf("ide: reset\n"); #endif + + if (s->pio_aiocb) { + bdrv_aio_cancel(s->pio_aiocb); + s->pio_aiocb = NULL; + } + if (s->drive_kind == IDE_CFATA) s->mult_sectors = 0; else diff --git a/hw/ide/internal.h b/hw/ide/internal.h index 100efd3..f8a027d 100644 --- a/hw/ide/internal.h +++ b/hw/ide/internal.h @@ -385,6 +385,9 @@ struct IDEState { int cd_sector_size; int atapi_dma; /* true if dma is requested for the packet cmd */ BlockAcctCookie acct; + BlockDriverAIOCB *pio_aiocb; + struct iovec iov; + QEMUIOVector qiov; /* ATA DMA state */ int io_buffer_size; QEMUSGList sg; diff --git a/hw/m48t59.c b/hw/m48t59.c index 60bbb00..0c50f45 100644 --- a/hw/m48t59.c +++ b/hw/m48t59.c @@ -239,7 +239,7 @@ void m48t59_write (void *opaque, uint32_t addr, uint32_t val) break; case 0x1FF5: /* alarm date */ - tmp = from_bcd(val & 0x1F); + tmp = from_bcd(val & 0x3F); if (tmp != 0) { NVRAM->alarm.tm_mday = tmp; NVRAM->buffer[0x1FF5] = val; @@ -310,8 +310,8 @@ void m48t59_write (void *opaque, uint32_t addr, uint32_t val) break; case 0x1FFD: case 0x07FD: - /* date */ - tmp = from_bcd(val & 0x1F); + /* date (BCD) */ + tmp = from_bcd(val & 0x3F); if (tmp != 0) { get_time(NVRAM, &tm); tm.tm_mday = tmp; @@ -916,7 +916,7 @@ static DeviceState *apic_init(void *env, uint8_t apic_id) msi_supported = true; } - if (xen_enabled()) { + if (xen_msi_support()) { msi_supported = true; } diff --git a/hw/pflash_cfi01.c b/hw/pflash_cfi01.c index b03f623..d1c7423 100644 --- a/hw/pflash_cfi01.c +++ b/hw/pflash_cfi01.c @@ -144,7 +144,6 @@ static uint32_t pflash_read (pflash_t *pfl, target_phys_addr_t offset, } else { ret = p[offset]; ret |= p[offset + 1] << 8; - ret |= p[offset + 1] << 8; ret |= p[offset + 2] << 16; ret |= p[offset + 3] << 24; } @@ -1370,7 +1370,7 @@ async_common: case QXL_IO_DESTROY_SURFACE_WAIT: if (val >= NUM_SURFACES) { qxl_guest_bug(d, "QXL_IO_DESTROY_SURFACE (async=%d):" - "%d >= NUM_SURFACES", async, val); + "%" PRIu64 " >= NUM_SURFACES", async, val); goto cancel_async; } qxl_spice_destroy_surface_wait(d, val, async); @@ -127,7 +127,7 @@ typedef struct PCIQXLDevice { /* qxl.c */ void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL phys, int group_id); -void qxl_guest_bug(PCIQXLDevice *qxl, const char *msg, ...); +void qxl_guest_bug(PCIQXLDevice *qxl, const char *msg, ...) GCC_FMT_ATTR(2, 3); void qxl_spice_update_area(PCIQXLDevice *qxl, uint32_t surface_id, struct QXLRect *area, struct QXLRect *dirty_rects, diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c index 8e76c5d..dbdb99c 100644 --- a/hw/scsi-bus.c +++ b/hw/scsi-bus.c @@ -1200,6 +1200,7 @@ static const char *scsi_command_name(uint8_t cmd) [ UNMAP ] = "UNMAP", [ READ_TOC ] = "READ_TOC", [ REPORT_DENSITY_SUPPORT ] = "REPORT_DENSITY_SUPPORT", + [ SANITIZE ] = "SANITIZE", [ GET_CONFIGURATION ] = "GET_CONFIGURATION", [ LOG_SELECT ] = "LOG_SELECT", [ LOG_SENSE ] = "LOG_SENSE", @@ -1430,15 +1431,18 @@ static char *scsibus_get_dev_path(DeviceState *dev) SCSIDevice *d = DO_UPCAST(SCSIDevice, qdev, dev); DeviceState *hba = dev->parent_bus->parent; char *id = NULL; + char *path; if (hba && hba->parent_bus && hba->parent_bus->info->get_dev_path) { id = hba->parent_bus->info->get_dev_path(hba); } if (id) { - return g_strdup_printf("%s/%d:%d:%d", id, d->channel, d->id, d->lun); + path = g_strdup_printf("%s/%d:%d:%d", id, d->channel, d->id, d->lun); } else { - return g_strdup_printf("%d:%d:%d", d->channel, d->id, d->lun); + path = g_strdup_printf("%d:%d:%d", d->channel, d->id, d->lun); } + g_free(id); + return path; } static char *scsibus_get_fw_dev_path(DeviceState *dev) diff --git a/hw/scsi-defs.h b/hw/scsi-defs.h index 354ed7b..ca24192 100644 --- a/hw/scsi-defs.h +++ b/hw/scsi-defs.h @@ -78,6 +78,7 @@ #define READ_TOC 0x43 #define REPORT_DENSITY_SUPPORT 0x44 #define GET_CONFIGURATION 0x46 +#define SANITIZE 0x48 #define GET_EVENT_STATUS_NOTIFICATION 0x4a #define LOG_SELECT 0x4c #define LOG_SENSE 0x4d diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index 9949786..a029ab6 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -55,6 +55,7 @@ typedef struct SCSIDiskReq { uint64_t sector; uint32_t sector_count; uint32_t buflen; + bool started; struct iovec iov; QEMUIOVector qiov; BlockAcctCookie acct; @@ -153,14 +154,80 @@ static void scsi_disk_load_request(QEMUFile *f, SCSIRequest *req) qemu_iovec_init_external(&r->qiov, &r->iov, 1); } -static void scsi_dma_complete(void *opaque, int ret) +static void scsi_flush_complete(void * opaque, int ret) { SCSIDiskReq *r = (SCSIDiskReq *)opaque; SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); bdrv_acct_done(s->qdev.conf.bs, &r->acct); - if (ret) { + if (ret < 0) { + if (scsi_handle_rw_error(r, -ret)) { + goto done; + } + } + + scsi_req_complete(&r->req, GOOD); + +done: + if (!r->req.io_canceled) { + scsi_req_unref(&r->req); + } +} + +static bool scsi_is_cmd_fua(SCSICommand *cmd) +{ + switch (cmd->buf[0]) { + case READ_10: + case READ_12: + case READ_16: + case WRITE_10: + case WRITE_12: + case WRITE_16: + return (cmd->buf[1] & 8) != 0; + + case VERIFY_10: + case VERIFY_12: + case VERIFY_16: + case WRITE_VERIFY_10: + case WRITE_VERIFY_12: + case WRITE_VERIFY_16: + return true; + + case READ_6: + case WRITE_6: + default: + return false; + } +} + +static void scsi_write_do_fua(SCSIDiskReq *r) +{ + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); + + if (scsi_is_cmd_fua(&r->req.cmd)) { + bdrv_acct_start(s->qdev.conf.bs, &r->acct, 0, BDRV_ACCT_FLUSH); + r->req.aiocb = bdrv_aio_flush(s->qdev.conf.bs, scsi_flush_complete, r); + return; + } + + scsi_req_complete(&r->req, GOOD); + if (!r->req.io_canceled) { + scsi_req_unref(&r->req); + } +} + +static void scsi_dma_complete(void *opaque, int ret) +{ + SCSIDiskReq *r = (SCSIDiskReq *)opaque; + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); + + if (r->req.aiocb != NULL) { + r->req.aiocb = NULL; + bdrv_acct_done(s->qdev.conf.bs, &r->acct); + } + + if (ret < 0) { if (scsi_handle_rw_error(r, -ret)) { goto done; } @@ -168,10 +235,17 @@ static void scsi_dma_complete(void *opaque, int ret) r->sector += r->sector_count; r->sector_count = 0; - scsi_req_complete(&r->req, GOOD); + if (r->req.cmd.mode == SCSI_XFER_TO_DEV) { + scsi_write_do_fua(r); + return; + } else { + scsi_req_complete(&r->req, GOOD); + } done: - scsi_req_unref(&r->req); + if (!r->req.io_canceled) { + scsi_req_unref(&r->req); + } } static void scsi_read_complete(void * opaque, int ret) @@ -185,7 +259,7 @@ static void scsi_read_complete(void * opaque, int ret) bdrv_acct_done(s->qdev.conf.bs, &r->acct); } - if (ret) { + if (ret < 0) { if (scsi_handle_rw_error(r, -ret)) { goto done; } @@ -204,10 +278,12 @@ done: } } -static void scsi_flush_complete(void * opaque, int ret) +/* Actually issue a read to the block device. */ +static void scsi_do_read(void *opaque, int ret) { - SCSIDiskReq *r = (SCSIDiskReq *)opaque; + SCSIDiskReq *r = opaque; SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); + uint32_t n; if (r->req.aiocb != NULL) { r->req.aiocb = NULL; @@ -220,7 +296,17 @@ static void scsi_flush_complete(void * opaque, int ret) } } - scsi_req_complete(&r->req, GOOD); + if (r->req.sg) { + dma_acct_start(s->qdev.conf.bs, &r->acct, r->req.sg, BDRV_ACCT_READ); + r->req.resid -= r->req.sg->size; + r->req.aiocb = dma_bdrv_read(s->qdev.conf.bs, r->req.sg, r->sector, + scsi_dma_complete, r); + } else { + n = scsi_init_iovec(r, SCSI_DMA_BUF_SIZE); + bdrv_acct_start(s->qdev.conf.bs, &r->acct, n * BDRV_SECTOR_SIZE, BDRV_ACCT_READ); + r->req.aiocb = bdrv_aio_readv(s->qdev.conf.bs, r->sector, &r->qiov, n, + scsi_read_complete, r); + } done: if (!r->req.io_canceled) { @@ -233,11 +319,12 @@ static void scsi_read_data(SCSIRequest *req) { SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); - uint32_t n; + bool first; if (r->sector_count == (uint32_t)-1) { DPRINTF("Read buf_len=%zd\n", r->iov.iov_len); r->sector_count = 0; + r->started = true; scsi_req_data(&r->req, r->iov.iov_len); return; } @@ -264,16 +351,13 @@ static void scsi_read_data(SCSIRequest *req) return; } - if (r->req.sg) { - dma_acct_start(s->qdev.conf.bs, &r->acct, r->req.sg, BDRV_ACCT_READ); - r->req.resid -= r->req.sg->size; - r->req.aiocb = dma_bdrv_read(s->qdev.conf.bs, r->req.sg, r->sector, - scsi_dma_complete, r); + first = !r->started; + r->started = true; + if (first && scsi_is_cmd_fua(&r->req.cmd)) { + bdrv_acct_start(s->qdev.conf.bs, &r->acct, 0, BDRV_ACCT_FLUSH); + r->req.aiocb = bdrv_aio_flush(s->qdev.conf.bs, scsi_do_read, r); } else { - n = scsi_init_iovec(r, SCSI_DMA_BUF_SIZE); - bdrv_acct_start(s->qdev.conf.bs, &r->acct, n * BDRV_SECTOR_SIZE, BDRV_ACCT_READ); - r->req.aiocb = bdrv_aio_readv(s->qdev.conf.bs, r->sector, &r->qiov, n, - scsi_read_complete, r); + scsi_do_read(r, 0); } } @@ -333,7 +417,7 @@ static void scsi_write_complete(void * opaque, int ret) bdrv_acct_done(s->qdev.conf.bs, &r->acct); } - if (ret) { + if (ret < 0) { if (scsi_handle_rw_error(r, -ret)) { goto done; } @@ -343,7 +427,8 @@ static void scsi_write_complete(void * opaque, int ret) r->sector += n; r->sector_count -= n; if (r->sector_count == 0) { - scsi_req_complete(&r->req, GOOD); + scsi_write_do_fua(r); + return; } else { scsi_init_iovec(r, SCSI_DMA_BUF_SIZE); DPRINTF("Write complete tag=0x%x more=%d\n", r->req.tag, r->qiov.size); @@ -375,6 +460,7 @@ static void scsi_write_data(SCSIRequest *req) if (!r->req.sg && !r->qiov.size) { /* Called for the first time. Ask the driver to send us more data. */ + r->started = true; scsi_write_complete(r, 0); return; } @@ -383,6 +469,16 @@ static void scsi_write_data(SCSIRequest *req) return; } + if (r->req.cmd.buf[0] == VERIFY_10 || r->req.cmd.buf[0] == VERIFY_12 || + r->req.cmd.buf[0] == VERIFY_16) { + if (r->req.sg) { + scsi_dma_complete(r, 0); + } else { + scsi_write_complete(r, 0); + } + return; + } + if (r->req.sg) { dma_acct_start(s->qdev.conf.bs, &r->acct, r->req.sg, BDRV_ACCT_WRITE); r->req.resid -= r->req.sg->size; @@ -531,8 +627,8 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) { outbuf[3] = buflen = 8; outbuf[4] = 0; - outbuf[5] = 0x40; /* write same with unmap supported */ - outbuf[6] = 0; + outbuf[5] = 0x60; /* write_same 10/16 supported */ + outbuf[6] = s->qdev.conf.discard_granularity ? 2 : 1; outbuf[7] = 0; break; } @@ -984,11 +1080,12 @@ static int scsi_disk_emulate_mode_sense(SCSIDiskReq *r, uint8_t *outbuf) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); uint64_t nb_sectors; - int page, dbd, buflen, ret, page_control; + bool dbd; + int page, buflen, ret, page_control; uint8_t *p; uint8_t dev_specific_param; - dbd = r->req.cmd.buf[1] & 0x8; + dbd = (r->req.cmd.buf[1] & 0x8) != 0; page = r->req.cmd.buf[2] & 0x3f; page_control = (r->req.cmd.buf[2] & 0xc0) >> 6; DPRINTF("Mode Sense(%d) (page %d, xfer %zd, page_control %d)\n", @@ -996,10 +1093,16 @@ static int scsi_disk_emulate_mode_sense(SCSIDiskReq *r, uint8_t *outbuf) memset(outbuf, 0, r->req.cmd.xfer); p = outbuf; - if (bdrv_is_read_only(s->qdev.conf.bs)) { - dev_specific_param = 0x80; /* Readonly. */ + if (s->qdev.type == TYPE_DISK) { + dev_specific_param = 0x10; /* DPOFUA */ + if (bdrv_is_read_only(s->qdev.conf.bs)) { + dev_specific_param |= 0x80; /* Readonly. */ + } } else { + /* MMC prescribes that CD/DVD drives have no block descriptors, + * and defines no device-specific parameter. */ dev_specific_param = 0x00; + dbd = true; } if (r->req.cmd.buf[0] == MODE_SENSE) { @@ -1014,9 +1117,8 @@ static int scsi_disk_emulate_mode_sense(SCSIDiskReq *r, uint8_t *outbuf) p += 8; } - /* MMC prescribes that CD/DVD drives have no block descriptors. */ bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors); - if (!dbd && s->qdev.type == TYPE_DISK && nb_sectors) { + if (!dbd && nb_sectors) { if (r->req.cmd.buf[0] == MODE_SENSE) { outbuf[3] = 8; /* Block descriptor length */ } else { /* MODE_SENSE_10 */ @@ -1306,8 +1408,6 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r) } DPRINTF("Unsupported Service Action In\n"); goto illegal_request; - case VERIFY_10: - break; default: scsi_check_condition(r, SENSE_CODE(INVALID_OPCODE)); return -1; @@ -1391,7 +1491,6 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf) case MECHANISM_STATUS: case SERVICE_ACTION_IN_16: case REQUEST_SENSE: - case VERIFY_10: rc = scsi_disk_emulate_command(r); if (rc < 0) { return 0; @@ -1417,6 +1516,9 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf) r->sector = r->req.cmd.lba * (s->qdev.blocksize / 512); r->sector_count = len * (s->qdev.blocksize / 512); break; + case VERIFY_10: + case VERIFY_12: + case VERIFY_16: case WRITE_6: case WRITE_10: case WRITE_12: @@ -1456,10 +1558,11 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf) goto illegal_lba; } break; + case WRITE_SAME_10: case WRITE_SAME_16: len = r->req.cmd.xfer / s->qdev.blocksize; - DPRINTF("WRITE SAME(16) (sector %" PRId64 ", count %d)\n", + DPRINTF("WRITE SAME() (sector %" PRId64 ", count %d)\n", r->req.cmd.lba, len); if (r->req.cmd.lba > s->qdev.max_lba) { @@ -1767,6 +1870,9 @@ static SCSIRequest *scsi_block_new_request(SCSIDevice *d, uint32_t tag, case READ_10: case READ_12: case READ_16: + case VERIFY_10: + case VERIFY_12: + case VERIFY_16: case WRITE_6: case WRITE_10: case WRITE_12: diff --git a/hw/virtio-scsi.c b/hw/virtio-scsi.c index 45d54fa..e8328f4 100644 --- a/hw/virtio-scsi.c +++ b/hw/virtio-scsi.c @@ -129,12 +129,12 @@ typedef struct { VirtIOSCSIConf *conf; SCSIBus bus; - VirtQueue *ctrl_vq; - VirtQueue *event_vq; - VirtQueue *cmd_vq; uint32_t sense_size; uint32_t cdb_size; int resetting; + VirtQueue *ctrl_vq; + VirtQueue *event_vq; + VirtQueue *cmd_vqs[0]; } VirtIOSCSI; typedef struct VirtIOSCSIReq { @@ -240,7 +240,10 @@ static VirtIOSCSIReq *virtio_scsi_pop_req(VirtIOSCSI *s, VirtQueue *vq) static void virtio_scsi_save_request(QEMUFile *f, SCSIRequest *sreq) { VirtIOSCSIReq *req = sreq->hba_private; + uint32_t n = virtio_queue_get_id(req->vq) - 2; + assert(n < req->dev->conf->num_queues); + qemu_put_be32s(f, &n); qemu_put_buffer(f, (unsigned char *)&req->elem, sizeof(req->elem)); } @@ -249,10 +252,13 @@ static void *virtio_scsi_load_request(QEMUFile *f, SCSIRequest *sreq) SCSIBus *bus = sreq->bus; VirtIOSCSI *s = container_of(bus, VirtIOSCSI, bus); VirtIOSCSIReq *req; + uint32_t n; req = g_malloc(sizeof(*req)); + qemu_get_be32s(f, &n); + assert(n < s->conf->num_queues); qemu_get_buffer(f, (unsigned char *)&req->elem, sizeof(req->elem)); - virtio_scsi_parse_req(s, s->cmd_vq, req); + virtio_scsi_parse_req(s, s->cmd_vqs[n], req); scsi_req_ref(sreq); req->sreq = sreq; @@ -579,10 +585,12 @@ VirtIODevice *virtio_scsi_init(DeviceState *dev, VirtIOSCSIConf *proxyconf) { VirtIOSCSI *s; static int virtio_scsi_id; + size_t sz; + int i; + sz = sizeof(VirtIOSCSI) + proxyconf->num_queues * sizeof(VirtQueue *); s = (VirtIOSCSI *)virtio_common_init("virtio-scsi", VIRTIO_ID_SCSI, - sizeof(VirtIOSCSIConfig), - sizeof(VirtIOSCSI)); + sizeof(VirtIOSCSIConfig), sz); s->qdev = dev; s->conf = proxyconf; @@ -597,8 +605,10 @@ VirtIODevice *virtio_scsi_init(DeviceState *dev, VirtIOSCSIConf *proxyconf) virtio_scsi_handle_ctrl); s->event_vq = virtio_add_queue(&s->vdev, VIRTIO_SCSI_VQ_SIZE, NULL); - s->cmd_vq = virtio_add_queue(&s->vdev, VIRTIO_SCSI_VQ_SIZE, - virtio_scsi_handle_cmd); + for (i = 0; i < s->conf->num_queues; i++) { + s->cmd_vqs[i] = virtio_add_queue(&s->vdev, VIRTIO_SCSI_VQ_SIZE, + virtio_scsi_handle_cmd); + } scsi_bus_new(&s->bus, dev, &virtio_scsi_scsi_info); if (!dev->hotplugged) { diff --git a/hw/virtio.c b/hw/virtio.c index 064aecf..314abf8 100644 --- a/hw/virtio.c +++ b/hw/virtio.c @@ -624,6 +624,13 @@ int virtio_queue_get_num(VirtIODevice *vdev, int n) return vdev->vq[n].vring.num; } +int virtio_queue_get_id(VirtQueue *vq) +{ + VirtIODevice *vdev = vq->vdev; + assert(vq >= &vdev->vq[0] && vq < &vdev->vq[VIRTIO_PCI_QUEUE_MAX]); + return vq - &vdev->vq[0]; +} + void virtio_queue_notify_vq(VirtQueue *vq) { if (vq->vring.desc) { diff --git a/hw/virtio.h b/hw/virtio.h index 400c092..0aef7d1 100644 --- a/hw/virtio.h +++ b/hw/virtio.h @@ -229,6 +229,7 @@ target_phys_addr_t virtio_queue_get_ring_size(VirtIODevice *vdev, int n); uint16_t virtio_queue_get_last_avail_idx(VirtIODevice *vdev, int n); void virtio_queue_set_last_avail_idx(VirtIODevice *vdev, int n, uint16_t idx); VirtQueue *virtio_get_queue(VirtIODevice *vdev, int n); +int virtio_queue_get_id(VirtQueue *vq); EventNotifier *virtio_queue_get_guest_notifier(VirtQueue *vq); EventNotifier *virtio_queue_get_host_notifier(VirtQueue *vq); void virtio_queue_notify_vq(VirtQueue *vq); @@ -57,4 +57,14 @@ void xen_register_framebuffer(struct MemoryRegion *mr); # define HVM_MAX_VCPUS 32 #endif +static inline int xen_msi_support(void) +{ +#if defined(CONFIG_XEN_CTRL_INTERFACE_VERSION) \ + && CONFIG_XEN_CTRL_INTERFACE_VERSION >= 420 + return xen_enabled(); +#else + return 0; +#endif +} + #endif /* QEMU_HW_XEN_H */ diff --git a/hw/xen_common.h b/hw/xen_common.h index 0409ac7..7043c14 100644 --- a/hw/xen_common.h +++ b/hw/xen_common.h @@ -133,6 +133,21 @@ static inline int xc_fd(xc_interface *xen_xc) } #endif +/* Xen before 4.2 */ +#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 420 +static inline int xen_xc_hvm_inject_msi(XenXC xen_xc, domid_t dom, + uint64_t addr, uint32_t data) +{ + return -ENOSYS; +} +#else +static inline int xen_xc_hvm_inject_msi(XenXC xen_xc, domid_t dom, + uint64_t addr, uint32_t data) +{ + return xc_hvm_inject_msi(xen_xc, dom, addr, data); +} +#endif + void destroy_hvm_domain(void); #endif /* QEMU_HW_XEN_COMMON_H */ diff --git a/linux-aio.c b/linux-aio.c index 15261ec..fa0fbf3 100644 --- a/linux-aio.c +++ b/linux-aio.c @@ -214,7 +214,7 @@ void *laio_init(void) goto out_close_efd; qemu_aio_set_fd_handler(s->efd, qemu_laio_completion_cb, NULL, - qemu_laio_flush_cb, NULL, s); + qemu_laio_flush_cb, s); return s; @@ -18,7 +18,6 @@ #include "nbd.h" #include "block.h" -#include "block_int.h" #include "qemu-coroutine.h" @@ -78,12 +77,10 @@ /* That's all folks */ -#define read_sync(fd, buffer, size) nbd_wr_sync(fd, buffer, size, true) -#define write_sync(fd, buffer, size) nbd_wr_sync(fd, buffer, size, false) - -size_t nbd_wr_sync(int fd, void *buffer, size_t size, bool do_read) +ssize_t nbd_wr_sync(int fd, void *buffer, size_t size, bool do_read) { size_t offset = 0; + int err; if (qemu_in_coroutine()) { if (do_read) { @@ -102,12 +99,16 @@ size_t nbd_wr_sync(int fd, void *buffer, size_t size, bool do_read) len = send(fd, buffer + offset, size - offset, 0); } - if (len == -1) - errno = socket_error(); + if (len < 0) { + err = socket_error(); + + /* recoverable error */ + if (err == EINTR || (offset > 0 && err == EAGAIN)) { + continue; + } - /* recoverable error */ - if (len == -1 && (errno == EAGAIN || errno == EINTR)) { - continue; + /* unrecoverable error */ + return -err; } /* eof */ @@ -115,17 +116,32 @@ size_t nbd_wr_sync(int fd, void *buffer, size_t size, bool do_read) break; } - /* unrecoverable error */ - if (len == -1) { - return 0; - } - offset += len; } return offset; } +static ssize_t read_sync(int fd, void *buffer, size_t size) +{ + /* Sockets are kept in blocking mode in the negotiation phase. After + * that, a non-readable socket simply means that another thread stole + * our request/reply. Synchronization is done with recv_coroutine, so + * that this is coroutine-safe. + */ + return nbd_wr_sync(fd, buffer, size, true); +} + +static ssize_t write_sync(int fd, void *buffer, size_t size) +{ + int ret; + do { + /* For writes, we do expect the socket to be writable. */ + ret = nbd_wr_sync(fd, buffer, size, false); + } while (ret == -EAGAIN); + return ret; +} + static void combine_addr(char *buf, size_t len, const char* address, uint16_t port) { @@ -193,6 +209,7 @@ int unix_socket_outgoing(const char *path) static int nbd_send_negotiate(int csock, off_t size, uint32_t flags) { char buf[8 + 8 + 8 + 128]; + int rc; /* Negotiate [ 0 .. 7] passwd ("NBDMAGIC") @@ -202,6 +219,9 @@ static int nbd_send_negotiate(int csock, off_t size, uint32_t flags) [28 .. 151] reserved (0) */ + socket_set_block(csock); + rc = -EINVAL; + TRACE("Beginning negotiation."); memcpy(buf, "NBDMAGIC", 8); cpu_to_be64w((uint64_t*)(buf + 8), 0x00420281861253LL); @@ -213,13 +233,14 @@ static int nbd_send_negotiate(int csock, off_t size, uint32_t flags) if (write_sync(csock, buf, sizeof(buf)) != sizeof(buf)) { LOG("write failed"); - errno = EINVAL; - return -1; + goto fail; } TRACE("Negotiation succeeded."); - - return 0; + rc = 0; +fail: + socket_set_nonblock(csock); + return rc; } int nbd_receive_negotiate(int csock, const char *name, uint32_t *flags, @@ -228,20 +249,22 @@ int nbd_receive_negotiate(int csock, const char *name, uint32_t *flags, char buf[256]; uint64_t magic, s; uint16_t tmp; + int rc; TRACE("Receiving negotiation."); + socket_set_block(csock); + rc = -EINVAL; + if (read_sync(csock, buf, 8) != 8) { LOG("read failed"); - errno = EINVAL; - return -1; + goto fail; } buf[8] = '\0'; if (strlen(buf) == 0) { LOG("server connection closed"); - errno = EINVAL; - return -1; + goto fail; } TRACE("Magic is %c%c%c%c%c%c%c%c", @@ -256,14 +279,12 @@ int nbd_receive_negotiate(int csock, const char *name, uint32_t *flags, if (memcmp(buf, "NBDMAGIC", 8) != 0) { LOG("Invalid magic received"); - errno = EINVAL; - return -1; + goto fail; } if (read_sync(csock, &magic, sizeof(magic)) != sizeof(magic)) { LOG("read failed"); - errno = EINVAL; - return -1; + goto fail; } magic = be64_to_cpu(magic); TRACE("Magic is 0x%" PRIx64, magic); @@ -276,61 +297,52 @@ int nbd_receive_negotiate(int csock, const char *name, uint32_t *flags, TRACE("Checking magic (opts_magic)"); if (magic != 0x49484156454F5054LL) { LOG("Bad magic received"); - errno = EINVAL; - return -1; + goto fail; } if (read_sync(csock, &tmp, sizeof(tmp)) != sizeof(tmp)) { LOG("flags read failed"); - errno = EINVAL; - return -1; + goto fail; } *flags = be16_to_cpu(tmp) << 16; /* reserved for future use */ if (write_sync(csock, &reserved, sizeof(reserved)) != sizeof(reserved)) { LOG("write failed (reserved)"); - errno = EINVAL; - return -1; + goto fail; } /* write the export name */ magic = cpu_to_be64(magic); if (write_sync(csock, &magic, sizeof(magic)) != sizeof(magic)) { LOG("write failed (magic)"); - errno = EINVAL; - return -1; + goto fail; } opt = cpu_to_be32(NBD_OPT_EXPORT_NAME); if (write_sync(csock, &opt, sizeof(opt)) != sizeof(opt)) { LOG("write failed (opt)"); - errno = EINVAL; - return -1; + goto fail; } namesize = cpu_to_be32(strlen(name)); if (write_sync(csock, &namesize, sizeof(namesize)) != sizeof(namesize)) { LOG("write failed (namesize)"); - errno = EINVAL; - return -1; + goto fail; } if (write_sync(csock, (char*)name, strlen(name)) != strlen(name)) { LOG("write failed (name)"); - errno = EINVAL; - return -1; + goto fail; } } else { TRACE("Checking magic (cli_magic)"); if (magic != 0x00420281861253LL) { LOG("Bad magic received"); - errno = EINVAL; - return -1; + goto fail; } } if (read_sync(csock, &s, sizeof(s)) != sizeof(s)) { LOG("read failed"); - errno = EINVAL; - return -1; + goto fail; } *size = be64_to_cpu(s); *blocksize = 1024; @@ -339,24 +351,25 @@ int nbd_receive_negotiate(int csock, const char *name, uint32_t *flags, if (!name) { if (read_sync(csock, flags, sizeof(*flags)) != sizeof(*flags)) { LOG("read failed (flags)"); - errno = EINVAL; - return -1; + goto fail; } *flags = be32_to_cpup(flags); } else { if (read_sync(csock, &tmp, sizeof(tmp)) != sizeof(tmp)) { LOG("read failed (tmp)"); - errno = EINVAL; - return -1; + goto fail; } *flags |= be32_to_cpu(tmp); } if (read_sync(csock, &buf, 124) != 124) { LOG("read failed (buf)"); - errno = EINVAL; - return -1; + goto fail; } - return 0; + rc = 0; + +fail: + socket_set_nonblock(csock); + return rc; } #ifdef __linux__ @@ -364,29 +377,26 @@ int nbd_init(int fd, int csock, uint32_t flags, off_t size, size_t blocksize) { TRACE("Setting NBD socket"); - if (ioctl(fd, NBD_SET_SOCK, csock) == -1) { + if (ioctl(fd, NBD_SET_SOCK, csock) < 0) { int serrno = errno; LOG("Failed to set NBD socket"); - errno = serrno; - return -1; + return -serrno; } TRACE("Setting block size to %lu", (unsigned long)blocksize); - if (ioctl(fd, NBD_SET_BLKSIZE, blocksize) == -1) { + if (ioctl(fd, NBD_SET_BLKSIZE, blocksize) < 0) { int serrno = errno; LOG("Failed setting NBD block size"); - errno = serrno; - return -1; + return -serrno; } TRACE("Setting size to %zd block(s)", (size_t)(size / blocksize)); - if (ioctl(fd, NBD_SET_SIZE_BLOCKS, size / blocksize) == -1) { + if (ioctl(fd, NBD_SET_SIZE_BLOCKS, size / blocksize) < 0) { int serrno = errno; LOG("Failed setting size (in blocks)"); - errno = serrno; - return -1; + return -serrno; } if (flags & NBD_FLAG_READ_ONLY) { @@ -396,8 +406,7 @@ int nbd_init(int fd, int csock, uint32_t flags, off_t size, size_t blocksize) if (ioctl(fd, BLKROSET, (unsigned long) &read_only) < 0) { int serrno = errno; LOG("Failed setting read-only attribute"); - errno = serrno; - return -1; + return -serrno; } } @@ -405,8 +414,7 @@ int nbd_init(int fd, int csock, uint32_t flags, off_t size, size_t blocksize) && errno != ENOTTY) { int serrno = errno; LOG("Failed setting flags"); - errno = serrno; - return -1; + return -serrno; } TRACE("Negotiation ended"); @@ -430,7 +438,7 @@ int nbd_client(int fd) TRACE("Doing NBD loop"); ret = ioctl(fd, NBD_DO_IT); - if (ret == -1 && errno == EPIPE) { + if (ret < 0 && errno == EPIPE) { /* NBD_DO_IT normally returns EPIPE when someone has disconnected * the socket via NBD_DISCONNECT. We do not want to return 1 in * that case. @@ -453,26 +461,24 @@ int nbd_client(int fd) #else int nbd_init(int fd, int csock, uint32_t flags, off_t size, size_t blocksize) { - errno = ENOTSUP; - return -1; + return -ENOTSUP; } int nbd_disconnect(int fd) { - errno = ENOTSUP; - return -1; + return -ENOTSUP; } int nbd_client(int fd) { - errno = ENOTSUP; - return -1; + return -ENOTSUP; } #endif -int nbd_send_request(int csock, struct nbd_request *request) +ssize_t nbd_send_request(int csock, struct nbd_request *request) { uint8_t buf[4 + 4 + 8 + 8 + 4]; + ssize_t ret; cpu_to_be32w((uint32_t*)buf, NBD_REQUEST_MAGIC); cpu_to_be32w((uint32_t*)(buf + 4), request->type); @@ -484,23 +490,32 @@ int nbd_send_request(int csock, struct nbd_request *request) "{ .from = %" PRIu64", .len = %u, .handle = %" PRIu64", .type=%i}", request->from, request->len, request->handle, request->type); - if (write_sync(csock, buf, sizeof(buf)) != sizeof(buf)) { + ret = write_sync(csock, buf, sizeof(buf)); + if (ret < 0) { + return ret; + } + + if (ret != sizeof(buf)) { LOG("writing to socket failed"); - errno = EINVAL; - return -1; + return -EINVAL; } return 0; } -static int nbd_receive_request(int csock, struct nbd_request *request) +static ssize_t nbd_receive_request(int csock, struct nbd_request *request) { uint8_t buf[4 + 4 + 8 + 8 + 4]; uint32_t magic; + ssize_t ret; - if (read_sync(csock, buf, sizeof(buf)) != sizeof(buf)) { + ret = read_sync(csock, buf, sizeof(buf)); + if (ret < 0) { + return ret; + } + + if (ret != sizeof(buf)) { LOG("read failed"); - errno = EINVAL; - return -1; + return -EINVAL; } /* Request @@ -523,23 +538,25 @@ static int nbd_receive_request(int csock, struct nbd_request *request) if (magic != NBD_REQUEST_MAGIC) { LOG("invalid magic (got 0x%x)", magic); - errno = EINVAL; - return -1; + return -EINVAL; } return 0; } -int nbd_receive_reply(int csock, struct nbd_reply *reply) +ssize_t nbd_receive_reply(int csock, struct nbd_reply *reply) { uint8_t buf[NBD_REPLY_SIZE]; uint32_t magic; + ssize_t ret; - memset(buf, 0xAA, sizeof(buf)); + ret = read_sync(csock, buf, sizeof(buf)); + if (ret < 0) { + return ret; + } - if (read_sync(csock, buf, sizeof(buf)) != sizeof(buf)) { + if (ret != sizeof(buf)) { LOG("read failed"); - errno = EINVAL; - return -1; + return -EINVAL; } /* Reply @@ -558,15 +575,15 @@ int nbd_receive_reply(int csock, struct nbd_reply *reply) if (magic != NBD_REPLY_MAGIC) { LOG("invalid magic (got 0x%x)", magic); - errno = EINVAL; - return -1; + return -EINVAL; } return 0; } -static int nbd_send_reply(int csock, struct nbd_reply *reply) +static ssize_t nbd_send_reply(int csock, struct nbd_reply *reply) { uint8_t buf[4 + 4 + 8]; + ssize_t ret; /* Reply [ 0 .. 3] magic (NBD_REPLY_MAGIC) @@ -579,10 +596,14 @@ static int nbd_send_reply(int csock, struct nbd_reply *reply) TRACE("Sending response to client"); - if (write_sync(csock, buf, sizeof(buf)) != sizeof(buf)) { + ret = write_sync(csock, buf, sizeof(buf)); + if (ret < 0) { + return ret; + } + + if (ret != sizeof(buf)) { LOG("writing to socket failed"); - errno = EINVAL; - return -1; + return -EINVAL; } return 0; } @@ -681,7 +702,7 @@ NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset, exp->bs = bs; exp->dev_offset = dev_offset; exp->nbdflags = nbdflags; - exp->size = size == -1 ? exp->bs->total_sectors * 512 : size; + exp->size = size == -1 ? bdrv_getlength(bs) : size; return exp; } @@ -702,12 +723,12 @@ static int nbd_can_read(void *opaque); static void nbd_read(void *opaque); static void nbd_restart_write(void *opaque); -static int nbd_co_send_reply(NBDRequest *req, struct nbd_reply *reply, - int len) +static ssize_t nbd_co_send_reply(NBDRequest *req, struct nbd_reply *reply, + int len) { NBDClient *client = req->client; int csock = client->sock; - int rc, ret; + ssize_t rc, ret; qemu_co_mutex_lock(&client->send_lock); qemu_set_fd_handler2(csock, nbd_can_read, nbd_read, @@ -716,22 +737,15 @@ static int nbd_co_send_reply(NBDRequest *req, struct nbd_reply *reply, if (!len) { rc = nbd_send_reply(csock, reply); - if (rc == -1) { - rc = -errno; - } } else { socket_set_cork(csock, 1); rc = nbd_send_reply(csock, reply); - if (rc != -1) { + if (rc >= 0) { ret = qemu_co_send(csock, req->data, len); if (ret != len) { - errno = EIO; - rc = -1; + rc = -EIO; } } - if (rc == -1) { - rc = -errno; - } socket_set_cork(csock, 0); } @@ -741,15 +755,18 @@ static int nbd_co_send_reply(NBDRequest *req, struct nbd_reply *reply, return rc; } -static int nbd_co_receive_request(NBDRequest *req, struct nbd_request *request) +static ssize_t nbd_co_receive_request(NBDRequest *req, struct nbd_request *request) { NBDClient *client = req->client; int csock = client->sock; - int rc; + ssize_t rc; client->recv_coroutine = qemu_coroutine_self(); - if (nbd_receive_request(csock, request) == -1) { - rc = -EIO; + rc = nbd_receive_request(csock, request); + if (rc < 0) { + if (rc != -EAGAIN) { + rc = -EIO; + } goto out; } @@ -792,11 +809,14 @@ static void nbd_trip(void *opaque) NBDExport *exp = client->exp; struct nbd_request request; struct nbd_reply reply; - int ret; + ssize_t ret; TRACE("Reading request."); ret = nbd_co_receive_request(req, &request); + if (ret == -EAGAIN) { + goto done; + } if (ret == -EIO) { goto out; } @@ -822,6 +842,15 @@ static void nbd_trip(void *opaque) case NBD_CMD_READ: TRACE("Request type is READ"); + if (request.type & NBD_CMD_FLAG_FUA) { + ret = bdrv_co_flush(exp->bs); + if (ret < 0) { + LOG("flush failed"); + reply.error = -ret; + goto error_reply; + } + } + ret = bdrv_read(exp->bs, (request.from + exp->dev_offset) / 512, req->data, request.len / 512); if (ret < 0) { @@ -862,8 +891,9 @@ static void nbd_trip(void *opaque) } } - if (nbd_co_send_reply(req, &reply, 0) < 0) + if (nbd_co_send_reply(req, &reply, 0) < 0) { goto out; + } break; case NBD_CMD_DISC: TRACE("Request type is DISCONNECT"); @@ -877,9 +907,9 @@ static void nbd_trip(void *opaque) LOG("flush failed"); reply.error = -ret; } - - if (nbd_co_send_reply(req, &reply, 0) < 0) + if (nbd_co_send_reply(req, &reply, 0) < 0) { goto out; + } break; case NBD_CMD_TRIM: TRACE("Request type is TRIM"); @@ -889,21 +919,24 @@ static void nbd_trip(void *opaque) LOG("discard failed"); reply.error = -ret; } - if (nbd_co_send_reply(req, &reply, 0) < 0) + if (nbd_co_send_reply(req, &reply, 0) < 0) { goto out; + } break; default: LOG("invalid request type (%u) received", request.type); invalid_request: reply.error = -EINVAL; error_reply: - if (nbd_co_send_reply(req, &reply, 0) == -1) + if (nbd_co_send_reply(req, &reply, 0) < 0) { goto out; + } break; } TRACE("Request/Reply complete"); +done: nbd_request_put(req); return; @@ -941,7 +974,7 @@ NBDClient *nbd_client_new(NBDExport *exp, int csock, void (*close)(NBDClient *)) { NBDClient *client; - if (nbd_send_negotiate(csock, exp->size, exp->nbdflags) == -1) { + if (nbd_send_negotiate(csock, exp->size, exp->nbdflags) < 0) { return NULL; } client = g_malloc0(sizeof(NBDClient)); @@ -59,7 +59,7 @@ enum { #define NBD_BUFFER_SIZE (1024*1024) -size_t nbd_wr_sync(int fd, void *buffer, size_t size, bool do_read); +ssize_t nbd_wr_sync(int fd, void *buffer, size_t size, bool do_read); int tcp_socket_outgoing(const char *address, uint16_t port); int tcp_socket_incoming(const char *address, uint16_t port); int tcp_socket_outgoing_spec(const char *address_and_port); @@ -70,8 +70,8 @@ int unix_socket_incoming(const char *path); int nbd_receive_negotiate(int csock, const char *name, uint32_t *flags, off_t *size, size_t *blocksize); int nbd_init(int fd, int csock, uint32_t flags, off_t size, size_t blocksize); -int nbd_send_request(int csock, struct nbd_request *request); -int nbd_receive_reply(int csock, struct nbd_reply *reply); +ssize_t nbd_send_request(int csock, struct nbd_request *request); +ssize_t nbd_receive_reply(int csock, struct nbd_reply *reply); int nbd_client(int fd); int nbd_disconnect(int fd); diff --git a/posix-aio-compat.c b/posix-aio-compat.c index d311d13..68361f5 100644 --- a/posix-aio-compat.c +++ b/posix-aio-compat.c @@ -468,26 +468,37 @@ static int qemu_paio_error(struct qemu_paiocb *aiocb) return ret; } -static int posix_aio_process_queue(void *opaque) +static void posix_aio_read(void *opaque) { PosixAioState *s = opaque; struct qemu_paiocb *acb, **pacb; int ret; - int result = 0; + ssize_t len; + + /* read all bytes from signal pipe */ + for (;;) { + char bytes[16]; + + len = read(s->rfd, bytes, sizeof(bytes)); + if (len == -1 && errno == EINTR) + continue; /* try again */ + if (len == sizeof(bytes)) + continue; /* more to read */ + break; + } for(;;) { pacb = &s->first_aio; for(;;) { acb = *pacb; if (!acb) - return result; + return; ret = qemu_paio_error(acb); if (ret == ECANCELED) { /* remove the request */ *pacb = acb->next; qemu_aio_release(acb); - result = 1; } else if (ret != EINPROGRESS) { /* end of aio */ if (ret == 0) { @@ -507,35 +518,12 @@ static int posix_aio_process_queue(void *opaque) /* call the callback */ acb->common.cb(acb->common.opaque, ret); qemu_aio_release(acb); - result = 1; break; } else { pacb = &acb->next; } } } - - return result; -} - -static void posix_aio_read(void *opaque) -{ - PosixAioState *s = opaque; - ssize_t len; - - /* read all bytes from signal pipe */ - for (;;) { - char bytes[16]; - - len = read(s->rfd, bytes, sizeof(bytes)); - if (len == -1 && errno == EINTR) - continue; /* try again */ - if (len == sizeof(bytes)) - continue; /* more to read */ - break; - } - - posix_aio_process_queue(s); } static int posix_aio_flush(void *opaque) @@ -675,8 +663,7 @@ int paio_init(void) fcntl(s->rfd, F_SETFL, O_NONBLOCK); fcntl(s->wfd, F_SETFL, O_NONBLOCK); - qemu_aio_set_fd_handler(s->rfd, posix_aio_read, NULL, posix_aio_flush, - posix_aio_process_queue, s); + qemu_aio_set_fd_handler(s->rfd, posix_aio_read, NULL, posix_aio_flush, s); ret = pthread_attr_init(&attr); if (ret) @@ -41,11 +41,6 @@ void qemu_aio_release(void *p); /* Returns 1 if there are still outstanding AIO requests; 0 otherwise */ typedef int (AioFlushHandler)(void *opaque); -/* Runs all currently allowed AIO callbacks of completed requests in the - * respective AIO backend. Returns 0 if no requests was handled, non-zero - * if at least one queued request was handled. */ -typedef int (AioProcessQueue)(void *opaque); - /* Flush any pending AIO operation. This function will block until all * outstanding AIO operations have been completed or cancelled. */ void qemu_aio_flush(void); @@ -53,15 +48,10 @@ void qemu_aio_flush(void); /* Wait for a single AIO completion to occur. This function will wait * until a single AIO event has completed and it will ensure something * has moved before returning. This can issue new pending aio as - * result of executing I/O completion or bh callbacks. */ -void qemu_aio_wait(void); - -/* - * Runs all currently allowed AIO callbacks of completed requests. Returns 0 - * if no requests were handled, non-zero if at least one request was - * processed. - */ -int qemu_aio_process_queue(void); + * result of executing I/O completion or bh callbacks. + * + * Return whether there is still any pending AIO operation. */ +bool qemu_aio_wait(void); /* Register a file descriptor and associated callbacks. Behaves very similarly * to qemu_set_fd_handler2. Unlike qemu_set_fd_handler2, these callbacks will @@ -74,7 +64,6 @@ int qemu_aio_set_fd_handler(int fd, IOHandler *io_read, IOHandler *io_write, AioFlushHandler *io_flush, - AioProcessQueue *io_process_queue, void *opaque); #endif diff --git a/qemu-coroutine-sleep.c b/qemu-coroutine-sleep.c index fd65274..d7083ee 100644 --- a/qemu-coroutine-sleep.c +++ b/qemu-coroutine-sleep.c @@ -23,7 +23,6 @@ static void co_sleep_cb(void *opaque) { CoSleepCB *sleep_cb = opaque; - qemu_free_timer(sleep_cb->ts); qemu_coroutine_enter(sleep_cb->co, NULL); } @@ -35,4 +34,6 @@ void coroutine_fn co_sleep_ns(QEMUClock *clock, int64_t ns) sleep_cb.ts = qemu_new_timer(clock, SCALE_NS, co_sleep_cb, &sleep_cb); qemu_mod_timer(sleep_cb.ts, qemu_get_clock_ns(clock) + ns); qemu_coroutine_yield(); + qemu_del_timer(sleep_cb.ts); + qemu_free_timer(sleep_cb.ts); } @@ -117,12 +117,13 @@ static gboolean register_signal_handlers(void) static void usage(const char *cmd) { printf( -"Usage: %s -c <channel_opts>\n" +"Usage: %s [-m <method> -p <path>] [<options>]\n" "QEMU Guest Agent %s\n" "\n" " -m, --method transport method: one of unix-listen, virtio-serial, or\n" " isa-serial (virtio-serial is the default)\n" -" -p, --path device/socket path (%s is the default for virtio-serial)\n" +" -p, --path device/socket path (the default for virtio-serial is:\n" +" %s)\n" " -l, --logfile set logfile path, logs to stderr by default\n" " -f, --pidfile specify pidfile (default is %s)\n" " -v, --verbose log extra debugging information\n" @@ -131,7 +132,7 @@ static void usage(const char *cmd) #ifdef _WIN32 " -s, --service service commands: install, uninstall\n" #endif -" -b, --blacklist comma-separated list of RPCs to disable (no spaces, \"?\"" +" -b, --blacklist comma-separated list of RPCs to disable (no spaces, \"?\"\n" " to list available RPCs)\n" " -h, --help display this help and exit\n" "\n" @@ -66,8 +66,8 @@ static void help(void) " 'filename' is a disk image filename\n" " 'fmt' is the disk image format. It is guessed automatically in most cases\n" " 'cache' is the cache mode used to write the output disk image, the valid\n" - " options are: 'none', 'writeback' (default), 'writethrough', 'directsync'\n" - " and 'unsafe'\n" + " options are: 'none', 'writeback' (default, except for convert), 'writethrough',\n" + " 'directsync' and 'unsafe' (default for convert)\n" " 'size' is the disk image size in bytes. Optional suffixes\n" " 'k' or 'K' (kilobyte, 1024), 'M' (megabyte, 1024k), 'G' (gigabyte, 1024M)\n" " and T (terabyte, 1024G) are supported. 'b' is ignored.\n" @@ -15,6 +15,7 @@ #include <libgen.h> #include "qemu-common.h" +#include "main-loop.h" #include "block_int.h" #include "cmd.h" #include "trace/control.h" @@ -295,7 +296,7 @@ static int do_aio_readv(QEMUIOVector *qiov, int64_t offset, int *total) bdrv_aio_readv(bs, offset >> 9, qiov, qiov->size >> 9, aio_rw_done, &async_ret); while (async_ret == NOT_DONE) { - qemu_aio_wait(); + main_loop_wait(false); } *total = qiov->size; @@ -309,7 +310,7 @@ static int do_aio_writev(QEMUIOVector *qiov, int64_t offset, int *total) bdrv_aio_writev(bs, offset >> 9, qiov, qiov->size >> 9, aio_rw_done, &async_ret); while (async_ret == NOT_DONE) { - qemu_aio_wait(); + main_loop_wait(false); } *total = qiov->size; @@ -352,7 +353,7 @@ static int do_aio_multiwrite(BlockRequest* reqs, int num_reqs, int *total) } while (async_ret.num_done < num_reqs) { - qemu_aio_wait(); + main_loop_wait(false); } return async_ret.error < 0 ? async_ret.error : 1; @@ -1784,6 +1785,7 @@ static void usage(const char *name) " -g, --growable allow file to grow (only applies to protocols)\n" " -m, --misalign misalign allocations for O_DIRECT\n" " -k, --native-aio use kernel AIO implementation (on Linux only)\n" +" -t, --cache=MODE use the given cache mode for the image\n" " -T, --trace FILE enable trace events listed in the given file\n" " -h, --help display this help and exit\n" " -V, --version output version information and exit\n" @@ -1796,7 +1798,7 @@ int main(int argc, char **argv) { int readonly = 0; int growable = 0; - const char *sopt = "hVc:rsnmgkT:"; + const char *sopt = "hVc:rsnmgkt:T:"; const struct option lopt[] = { { "help", 0, NULL, 'h' }, { "version", 0, NULL, 'V' }, @@ -1808,6 +1810,7 @@ int main(int argc, char **argv) { "misalign", 0, NULL, 'm' }, { "growable", 0, NULL, 'g' }, { "native-aio", 0, NULL, 'k' }, + { "cache", 1, NULL, 't' }, { "trace", 1, NULL, 'T' }, { NULL, 0, NULL, 0 } }; @@ -1840,6 +1843,12 @@ int main(int argc, char **argv) case 'k': flags |= BDRV_O_NATIVE_AIO; break; + case 't': + if (bdrv_parse_cache_flags(optarg, &flags) < 0) { + error_report("Invalid cache option: %s", optarg); + exit(1); + } + break; case 'T': if (!trace_backend_init(optarg, NULL)) { exit(1); /* error message will have been printed */ @@ -17,7 +17,7 @@ */ #include "qemu-common.h" -#include "block_int.h" +#include "block.h" #include "nbd.h" #include <stdarg.h> @@ -126,8 +126,7 @@ static int find_partition(BlockDriverState *bs, int partition, } if (data[510] != 0x55 || data[511] != 0xaa) { - errno = -EINVAL; - return -1; + return -EINVAL; } for (i = 0; i < 4; i++) { @@ -165,8 +164,7 @@ static int find_partition(BlockDriverState *bs, int partition, } } - errno = -ENOENT; - return -1; + return -ENOENT; } static void termsig_handler(int signum) @@ -186,7 +184,7 @@ static void *show_parts(void *arg) * modprobe nbd max_part=63 */ nbd = open(device, O_RDWR); - if (nbd != -1) { + if (nbd >= 0) { close(nbd); } return NULL; @@ -203,25 +201,25 @@ static void *nbd_client_thread(void *arg) pthread_t show_parts_thread; sock = unix_socket_outgoing(sockpath); - if (sock == -1) { + if (sock < 0) { goto out; } ret = nbd_receive_negotiate(sock, NULL, &nbdflags, &size, &blocksize); - if (ret == -1) { + if (ret < 0) { goto out; } fd = open(device, O_RDWR); - if (fd == -1) { + if (fd < 0) { /* Linux-only, we can use %m in printf. */ fprintf(stderr, "Failed to open %s: %m", device); goto out; } ret = nbd_init(fd, sock, nbdflags, size, blocksize); - if (ret == -1) { + if (ret < 0) { goto out; } @@ -268,7 +266,7 @@ static void nbd_accept(void *opaque) int fd = accept(server_fd, (struct sockaddr *)&addr, &addr_len); nbd_started = true; - if (fd != -1 && nbd_client_new(exp, fd, nbd_client_closed)) { + if (fd >= 0 && nbd_client_new(exp, fd, nbd_client_closed)) { nb_fds++; } } @@ -410,9 +408,9 @@ int main(int argc, char **argv) if (disconnect) { fd = open(argv[optind], O_RDWR); - if (fd == -1) + if (fd < 0) { err(EXIT_FAILURE, "Cannot open %s", argv[optind]); - + } nbd_disconnect(fd); close(fd); @@ -427,7 +425,7 @@ int main(int argc, char **argv) pid_t pid; int ret; - if (qemu_pipe(stderr_fd) == -1) { + if (qemu_pipe(stderr_fd) < 0) { err(EXIT_FAILURE, "Error setting up communication pipe"); } @@ -441,7 +439,7 @@ int main(int argc, char **argv) /* Temporarily redirect stderr to the parent's pipe... */ dup2(stderr_fd[1], STDERR_FILENO); - if (ret == -1) { + if (ret < 0) { err(EXIT_FAILURE, "Failed to daemonize"); } @@ -459,11 +457,11 @@ int main(int argc, char **argv) while ((ret = read(stderr_fd[0], buf, 1024)) > 0) { errors = true; ret = qemu_write_full(STDERR_FILENO, buf, ret); - if (ret == -1) { + if (ret < 0) { exit(EXIT_FAILURE); } } - if (ret == -1) { + if (ret < 0) { err(EXIT_FAILURE, "Cannot read from daemon"); } @@ -489,11 +487,14 @@ int main(int argc, char **argv) err(EXIT_FAILURE, "Failed to bdrv_open '%s'", argv[optind]); } - fd_size = bs->total_sectors * 512; + fd_size = bdrv_getlength(bs); - if (partition != -1 && - find_partition(bs, partition, &dev_offset, &fd_size)) { - err(EXIT_FAILURE, "Could not find partition %d", partition); + if (partition != -1) { + ret = find_partition(bs, partition, &dev_offset, &fd_size); + if (ret < 0) { + errno = -ret; + err(EXIT_FAILURE, "Could not find partition %d", partition); + } } exp = nbd_export_new(bs, dev_offset, fd_size, nbdflags); @@ -504,7 +505,7 @@ int main(int argc, char **argv) fd = tcp_socket_incoming(bindto, port); } - if (fd == -1) { + if (fd < 0) { return 1; } diff --git a/qemu-tool.c b/qemu-tool.c index edb84f5..6579b00 100644 --- a/qemu-tool.c +++ b/qemu-tool.c @@ -61,7 +61,7 @@ void monitor_protocol_event(MonitorEvent event, QObject *data) int64_t cpu_get_clock(void) { - return 0; + return qemu_get_clock_ns(rt_clock); } int64_t cpu_get_icount(void) @@ -87,7 +87,6 @@ int qemu_init_main_loop(void) { init_clocks(); init_timer_alarm(); - qemu_clock_enable(vm_clock, false); return main_loop_init(); } diff --git a/qga/commands-posix.c b/qga/commands-posix.c index faf970d..087c3af 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -881,46 +881,50 @@ error: #else /* defined(__linux__) */ -GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **err) +void qmp_guest_suspend_disk(Error **err) { error_set(err, QERR_UNSUPPORTED); - - return 0; } -int64_t qmp_guest_fsfreeze_freeze(Error **err) +void qmp_guest_suspend_ram(Error **err) { error_set(err, QERR_UNSUPPORTED); - - return 0; } -int64_t qmp_guest_fsfreeze_thaw(Error **err) +void qmp_guest_suspend_hybrid(Error **err) { error_set(err, QERR_UNSUPPORTED); - - return 0; } -void qmp_guest_suspend_disk(Error **err) +GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp) { - error_set(err, QERR_UNSUPPORTED); + error_set(errp, QERR_UNSUPPORTED); + return NULL; } -void qmp_guest_suspend_ram(Error **err) +#endif + +#if !defined(CONFIG_FSFREEZE) + +GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **err) { error_set(err, QERR_UNSUPPORTED); + + return 0; } -void qmp_guest_suspend_hybrid(Error **err) +int64_t qmp_guest_fsfreeze_freeze(Error **err) { error_set(err, QERR_UNSUPPORTED); + + return 0; } -GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp) +int64_t qmp_guest_fsfreeze_thaw(Error **err) { - error_set(errp, QERR_UNSUPPORTED); - return NULL; + error_set(err, QERR_UNSUPPORTED); + + return 0; } #endif diff --git a/scripts/tracetool b/scripts/tracetool deleted file mode 100755 index 7b1c142..0000000 --- a/scripts/tracetool +++ /dev/null @@ -1,666 +0,0 @@ -#!/bin/sh -# -# Code generator for trace events -# -# Copyright IBM, Corp. 2010 -# -# This work is licensed under the terms of the GNU GPL, version 2. See -# the COPYING file in the top-level directory. - -# Disable pathname expansion, makes processing text with '*' characters simpler -set -f - -usage() -{ - cat >&2 <<EOF -usage: $0 [--nop | --simple | --stderr | --ust | --dtrace] [-h | -c] -Generate tracing code for a file on stdin. - -Backends: - --nop Tracing disabled - --simple Simple built-in backend - --stderr Stderr built-in backend - --ust LTTng User Space Tracing backend - --dtrace DTrace/SystemTAP backend - -Output formats: - -h Generate .h file - -c Generate .c file - -d Generate .d file (DTrace only) - --stap Generate .stp file (DTrace with SystemTAP only) - -Options: - --binary [path] Full path to QEMU binary - --target-arch [arch] QEMU emulator target arch - --target-type [type] QEMU emulator target type ('system' or 'user') - --probe-prefix [prefix] Prefix for dtrace probe names - (default: qemu-\$targettype-\$targetarch) - -EOF - exit 1 -} - -# Print a line without interpreting backslash escapes -# -# The built-in echo command may interpret backslash escapes without an option -# to disable this behavior. -puts() -{ - printf "%s\n" "$1" -} - -# Get the name of a trace event -get_name() -{ - local name - name=${1%%\(*} - echo "${name##* }" -} - -# Get the given property of a trace event -# 1: trace-events line -# 2: property name -# -> return 0 if property is present, or 1 otherwise -has_property() -{ - local props prop - props=${1%%\(*} - props=${props% *} - for prop in $props; do - if [ "$prop" = "$2" ]; then - return 0 - fi - done - return 1 -} - -# Get the argument list of a trace event, including types and names -get_args() -{ - local args - args=${1#*\(} - args=${args%%\)*} - echo "$args" - - if (echo "$args" | grep "[ *]next\($\|[, ]\)" > /dev/null 2>&1); then - echo -e "\n#error 'next' is a bad argument name (clash with systemtap keyword)\n " - fi -} - -# Get the argument name list of a trace event -get_argnames() -{ - local nfields field name sep - nfields=0 - sep="$2" - for field in $(get_args "$1"); do - nfields=$((nfields + 1)) - - # Drop pointer star - field=${field#\*} - - # Only argument names have commas at the end - name=${field%,} - test "$field" = "$name" && continue - - printf "%s%s " $name $sep - done - - # Last argument name - if [ "$nfields" -gt 1 ] - then - printf "%s" "$name" - fi -} - -# Get the number of arguments to a trace event -get_argc() -{ - local name argc - argc=0 - for name in $(get_argnames "$1", ","); do - argc=$((argc + 1)) - done - echo $argc -} - -# Get the format string including double quotes for a trace event -get_fmt() -{ - puts "${1#*)}" -} - -linetoh_begin_nop() -{ - return -} - -linetoh_nop() -{ - local name args - name=$(get_name "$1") - args=$(get_args "$1") - - # Define an empty function for the trace event - cat <<EOF -static inline void trace_$name($args) -{ -} -EOF -} - -linetoh_end_nop() -{ - return -} - -linetoc_begin_nop() -{ - return -} - -linetoc_nop() -{ - # No need for function definitions in nop backend - return -} - -linetod_nop() -{ - # Used when "disabled" events are processed - return -} - -linetostap_nop() -{ - # Used when "disabled" events are processed - return -} - -linetoc_end_nop() -{ - return -} - -linetoh_begin_simple() -{ - cat <<EOF -#include "trace/simple.h" -EOF - - simple_event_num=0 -} - -cast_args_to_uint64_t() -{ - local arg - for arg in $(get_argnames "$1", ","); do - printf "%s" "(uint64_t)(uintptr_t)$arg" - done -} - -linetoh_simple() -{ - local name args argc trace_args - name=$(get_name "$1") - args=$(get_args "$1") - argc=$(get_argc "$1") - - trace_args="$simple_event_num" - if [ "$argc" -gt 0 ] - then - trace_args="$trace_args, $(cast_args_to_uint64_t "$1")" - fi - - cat <<EOF -static inline void trace_$name($args) -{ - trace$argc($trace_args); -} -EOF - - simple_event_num=$((simple_event_num + 1)) -} - -linetoh_end_simple() -{ - cat <<EOF -#define NR_TRACE_EVENTS $simple_event_num -extern TraceEvent trace_list[NR_TRACE_EVENTS]; -EOF -} - -linetoc_begin_simple() -{ - cat <<EOF -#include "trace.h" - -TraceEvent trace_list[] = { -EOF - simple_event_num=0 - -} - -linetoc_simple() -{ - local name - name=$(get_name "$1") - cat <<EOF -{.tp_name = "$name", .state=0}, -EOF - simple_event_num=$((simple_event_num + 1)) -} - -linetoc_end_simple() -{ - cat <<EOF -}; -EOF -} - -#STDERR -linetoh_begin_stderr() -{ - cat <<EOF -#include <stdio.h> -#include "trace/stderr.h" - -extern TraceEvent trace_list[]; -EOF - - stderr_event_num=0 -} - -linetoh_stderr() -{ - local name args argnames argc fmt - name=$(get_name "$1") - args=$(get_args "$1") - argnames=$(get_argnames "$1" ",") - argc=$(get_argc "$1") - fmt=$(get_fmt "$1") - - if [ "$argc" -gt 0 ]; then - argnames=", $argnames" - fi - - cat <<EOF -static inline void trace_$name($args) -{ - if (trace_list[$stderr_event_num].state != 0) { - fprintf(stderr, "$name " $fmt "\n" $argnames); - } -} -EOF - stderr_event_num=$((stderr_event_num + 1)) - -} - -linetoh_end_stderr() -{ - cat <<EOF -#define NR_TRACE_EVENTS $stderr_event_num -EOF -} - -linetoc_begin_stderr() -{ - cat <<EOF -#include "trace.h" - -TraceEvent trace_list[] = { -EOF - stderr_event_num=0 -} - -linetoc_stderr() -{ - local name - name=$(get_name "$1") - cat <<EOF -{.tp_name = "$name", .state=0}, -EOF - stderr_event_num=$(($stderr_event_num + 1)) -} - -linetoc_end_stderr() -{ - cat <<EOF -}; -EOF -} -#END OF STDERR - -# Clean up after UST headers which pollute the namespace -ust_clean_namespace() { - cat <<EOF -#undef mutex_lock -#undef mutex_unlock -#undef inline -#undef wmb -EOF -} - -linetoh_begin_ust() -{ - echo "#include <ust/tracepoint.h>" - ust_clean_namespace -} - -linetoh_ust() -{ - local name args argnames - name=$(get_name "$1") - args=$(get_args "$1") - argnames=$(get_argnames "$1", ",") - - cat <<EOF -DECLARE_TRACE(ust_$name, TP_PROTO($args), TP_ARGS($argnames)); -#define trace_$name trace_ust_$name -EOF -} - -linetoh_end_ust() -{ - return -} - -linetoc_begin_ust() -{ - cat <<EOF -#include <ust/marker.h> -$(ust_clean_namespace) -#include "trace.h" -EOF -} - -linetoc_ust() -{ - local name args argnames fmt - name=$(get_name "$1") - args=$(get_args "$1") - argnames=$(get_argnames "$1", ",") - [ -z "$argnames" ] || argnames=", $argnames" - fmt=$(get_fmt "$1") - - cat <<EOF -DEFINE_TRACE(ust_$name); - -static void ust_${name}_probe($args) -{ - trace_mark(ust, $name, $fmt$argnames); -} -EOF - - # Collect names for later - names="$names $name" -} - -linetoc_end_ust() -{ - cat <<EOF -static void __attribute__((constructor)) trace_init(void) -{ -EOF - - for name in $names; do - cat <<EOF - register_trace_ust_$name(ust_${name}_probe); -EOF - done - - echo "}" -} - -linetoh_begin_dtrace() -{ - cat <<EOF -#include "trace-dtrace.h" -EOF -} - -linetoh_dtrace() -{ - local name args argnames nameupper - name=$(get_name "$1") - args=$(get_args "$1") - argnames=$(get_argnames "$1", ",") - - nameupper=`echo $name | LC_ALL=C tr '[a-z]' '[A-Z]'` - - # Define an empty function for the trace event - cat <<EOF -static inline void trace_$name($args) { - QEMU_${nameupper}($argnames); -} -EOF -} - -linetoh_end_dtrace() -{ - return -} - -linetoc_begin_dtrace() -{ - return -} - -linetoc_dtrace() -{ - # No need for function definitions in dtrace backend - return -} - -linetoc_end_dtrace() -{ - return -} - -linetod_begin_dtrace() -{ - cat <<EOF -provider qemu { -EOF -} - -linetod_dtrace() -{ - local name args - name=$(get_name "$1") - args=$(get_args "$1") - - # DTrace provider syntax expects foo() for empty - # params, not foo(void) - if [ "$args" = "void" ]; then - args="" - fi - - # Define prototype for probe arguments - cat <<EOF - probe $name($args); -EOF -} - -linetod_end_dtrace() -{ - cat <<EOF -}; -EOF -} - -linetostap_begin_dtrace() -{ - return -} - -linetostap_dtrace() -{ - local i arg name args arglist - name=$(get_name "$1") - args=$(get_args "$1") - arglist=$(get_argnames "$1", "") - - # Define prototype for probe arguments - cat <<EOF -probe $probeprefix.$name = process("$binary").mark("$name") -{ -EOF - - i=1 - for arg in $arglist - do - # postfix reserved words with '_' - case "$arg" in - limit|in|next|self) - arg="${arg}_" - ;; - esac - cat <<EOF - $arg = \$arg$i; -EOF - i="$((i+1))" - done - - cat <<EOF -} -EOF -} - -linetostap_end_dtrace() -{ - return -} - -# Process stdin by calling begin, line, and end functions for the backend -convert() -{ - local begin process_line end str name NAME enabled - begin="lineto$1_begin_$backend" - process_line="lineto$1_$backend" - end="lineto$1_end_$backend" - - "$begin" - - while read -r str; do - # Skip comments and empty lines - test -z "${str%%#*}" && continue - - echo - # Process the line. The nop backend handles disabled lines. - if has_property "$str" "disable"; then - "lineto$1_nop" "$str" - enabled=0 - else - "$process_line" "$str" - enabled=1 - fi - if [ "$1" = "h" ]; then - name=$(get_name "$str") - NAME=$(echo $name | LC_ALL=C tr '[a-z]' '[A-Z]') - echo "#define TRACE_${NAME}_ENABLED ${enabled}" - fi - done - - echo - "$end" -} - -tracetoh() -{ - cat <<EOF -#ifndef TRACE_H -#define TRACE_H - -/* This file is autogenerated by tracetool, do not edit. */ - -#include "qemu-common.h" -EOF - convert h - echo "#endif /* TRACE_H */" -} - -tracetoc() -{ - echo "/* This file is autogenerated by tracetool, do not edit. */" - convert c -} - -tracetod() -{ - if [ $backend != "dtrace" ]; then - echo "DTrace probe generator not applicable to $backend backend" - exit 1 - fi - echo "/* This file is autogenerated by tracetool, do not edit. */" - convert d -} - -tracetostap() -{ - if [ $backend != "dtrace" ]; then - echo "SystemTAP tapset generator not applicable to $backend backend" - exit 1 - fi - if [ -z "$binary" ]; then - echo "--binary is required for SystemTAP tapset generator" - exit 1 - fi - if [ -z "$probeprefix" -a -z "$targettype" ]; then - echo "--target-type is required for SystemTAP tapset generator" - exit 1 - fi - if [ -z "$probeprefix" -a -z "$targetarch" ]; then - echo "--target-arch is required for SystemTAP tapset generator" - exit 1 - fi - if [ -z "$probeprefix" ]; then - probeprefix="qemu.$targettype.$targetarch"; - fi - echo "/* This file is autogenerated by tracetool, do not edit. */" - convert stap -} - - -backend= -output= -binary= -targettype= -targetarch= -probeprefix= - - -until [ -z "$1" ] -do - case "$1" in - "--nop" | "--simple" | "--stderr" | "--ust" | "--dtrace") backend="${1#--}" ;; - - "--binary") shift ; binary="$1" ;; - "--target-arch") shift ; targetarch="$1" ;; - "--target-type") shift ; targettype="$1" ;; - "--probe-prefix") shift ; probeprefix="$1" ;; - - "-h" | "-c" | "-d") output="${1#-}" ;; - "--stap") output="${1#--}" ;; - - "--check-backend") exit 0 ;; # used by ./configure to test for backend - - "--list-backends") # used by ./configure to list available backends - echo "nop simple stderr ust dtrace" - exit 0 - ;; - - *) - usage;; - esac - shift -done - -if [ "$backend" = "" -o "$output" = "" ]; then - usage -fi - -gen="traceto$output" -"$gen" - -exit 0 diff --git a/scripts/tracetool.py b/scripts/tracetool.py new file mode 100755 index 0000000..cacfd99 --- /dev/null +++ b/scripts/tracetool.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Command-line wrapper for the tracetool machinery. +""" + +__author__ = "LluÃs Vilanova <vilanova@ac.upc.edu>" +__copyright__ = "Copyright 2012, LluÃs Vilanova <vilanova@ac.upc.edu>" +__license__ = "GPL version 2 or (at your option) any later version" + +__maintainer__ = "Stefan Hajnoczi" +__email__ = "stefanha@linux.vnet.ibm.com" + + +import sys +import getopt + +from tracetool import error_write, out +import tracetool.backend +import tracetool.format + + +_SCRIPT = "" + +def error_opt(msg = None): + if msg is not None: + error_write("Error: " + msg + "\n") + + backend_descr = "\n".join([ " %-15s %s" % (n, d) + for n,d in tracetool.backend.get_list() ]) + format_descr = "\n".join([ " %-15s %s" % (n, d) + for n,d in tracetool.format.get_list() ]) + error_write("""\ +Usage: %(script)s --format=<format> --backend=<backend> [<options>] + +Backends: +%(backends)s + +Formats: +%(formats)s + +Options: + --help This help message. + --list-backends Print list of available backends. + --check-backend Check if the given backend is valid. + --binary <path> Full path to QEMU binary. + --target-type <type> QEMU emulator target type ('system' or 'user'). + --target-arch <arch> QEMU emulator target arch. + --probe-prefix <prefix> Prefix for dtrace probe names + (default: qemu-<target-type>-<target-arch>).\ +""" % { + "script" : _SCRIPT, + "backends" : backend_descr, + "formats" : format_descr, + }) + + if msg is None: + sys.exit(0) + else: + sys.exit(1) + + +def main(args): + global _SCRIPT + _SCRIPT = args[0] + + long_opts = [ "backend=", "format=", "help", "list-backends", "check-backend" ] + long_opts += [ "binary=", "target-type=", "target-arch=", "probe-prefix=" ] + + try: + opts, args = getopt.getopt(args[1:], "", long_opts) + except getopt.GetoptError as err: + error_opt(str(err)) + + check_backend = False + arg_backend = "" + arg_format = "" + binary = None + target_type = None + target_arch = None + probe_prefix = None + for opt, arg in opts: + if opt == "--help": + error_opt() + + elif opt == "--backend": + arg_backend = arg + elif opt == "--format": + arg_format = arg + + elif opt == "--list-backends": + backends = tracetool.backend.get_list() + out(", ".join([ b for b,_ in backends ])) + sys.exit(0) + elif opt == "--check-backend": + check_backend = True + + elif opt == "--binary": + binary = arg + elif opt == '--target-type': + target_type = arg + elif opt == '--target-arch': + target_arch = arg + elif opt == '--probe-prefix': + probe_prefix = arg + + else: + error_opt("unhandled option: %s" % opt) + + if arg_backend is None: + error_opt("backend not set") + + if check_backend: + if tracetool.backend.exists(arg_backend): + sys.exit(0) + else: + sys.exit(1) + + if arg_format == "stap": + if binary is None: + error_opt("--binary is required for SystemTAP tapset generator") + if probe_prefix is None and target_type is None: + error_opt("--target-type is required for SystemTAP tapset generator") + if probe_prefix is None and target_arch is None: + error_opt("--target-arch is required for SystemTAP tapset generator") + + if probe_prefix is None: + probe_prefix = ".".join([ "qemu", target_type, target_arch ]) + + try: + tracetool.generate(sys.stdin, arg_format, arg_backend, + binary = binary, probe_prefix = probe_prefix) + except tracetool.TracetoolError as e: + error_opt(str(e)) + +if __name__ == "__main__": + main(sys.argv) diff --git a/scripts/tracetool/__init__.py b/scripts/tracetool/__init__.py new file mode 100644 index 0000000..74fe21b --- /dev/null +++ b/scripts/tracetool/__init__.py @@ -0,0 +1,271 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Machinery for generating tracing-related intermediate files. +""" + +__author__ = "LluÃs Vilanova <vilanova@ac.upc.edu>" +__copyright__ = "Copyright 2012, LluÃs Vilanova <vilanova@ac.upc.edu>" +__license__ = "GPL version 2 or (at your option) any later version" + +__maintainer__ = "Stefan Hajnoczi" +__email__ = "stefanha@linux.vnet.ibm.com" + + +import re +import sys + +import tracetool.format +import tracetool.backend + + +def error_write(*lines): + """Write a set of error lines.""" + sys.stderr.writelines("\n".join(lines) + "\n") + +def error(*lines): + """Write a set of error lines and exit.""" + error_write(*lines) + sys.exit(1) + + +def out(*lines, **kwargs): + """Write a set of output lines. + + You can use kwargs as a shorthand for mapping variables when formating all + the strings in lines. + """ + lines = [ l % kwargs for l in lines ] + sys.stdout.writelines("\n".join(lines) + "\n") + + +class Arguments: + """Event arguments description.""" + + def __init__(self, args): + """ + Parameters + ---------- + args : + List of (type, name) tuples. + """ + self._args = args + + @staticmethod + def build(arg_str): + """Build and Arguments instance from an argument string. + + Parameters + ---------- + arg_str : str + String describing the event arguments. + """ + res = [] + for arg in arg_str.split(","): + arg = arg.strip() + parts = arg.split() + head, sep, tail = parts[-1].rpartition("*") + parts = parts[:-1] + if tail == "void": + assert len(parts) == 0 and sep == "" + continue + arg_type = " ".join(parts + [ " ".join([head, sep]).strip() ]).strip() + res.append((arg_type, tail)) + return Arguments(res) + + def __iter__(self): + """Iterate over the (type, name) pairs.""" + return iter(self._args) + + def __len__(self): + """Number of arguments.""" + return len(self._args) + + def __str__(self): + """String suitable for declaring function arguments.""" + if len(self._args) == 0: + return "void" + else: + return ", ".join([ " ".join([t, n]) for t,n in self._args ]) + + def __repr__(self): + """Evaluable string representation for this object.""" + return "Arguments(\"%s\")" % str(self) + + def names(self): + """List of argument names.""" + return [ name for _, name in self._args ] + + def types(self): + """List of argument types.""" + return [ type_ for type_, _ in self._args ] + + +class Event(object): + """Event description. + + Attributes + ---------- + name : str + The event name. + fmt : str + The event format string. + properties : set(str) + Properties of the event. + args : Arguments + The event arguments. + """ + + _CRE = re.compile("((?P<props>.*)\s+)?(?P<name>[^(\s]+)\((?P<args>[^)]*)\)\s*(?P<fmt>\".*)?") + + _VALID_PROPS = set(["disable"]) + + def __init__(self, name, props, fmt, args): + """ + Parameters + ---------- + name : string + Event name. + props : list of str + Property names. + fmt : str + Event printing format. + args : Arguments + Event arguments. + """ + self.name = name + self.properties = props + self.fmt = fmt + self.args = args + + unknown_props = set(self.properties) - self._VALID_PROPS + if len(unknown_props) > 0: + raise ValueError("Unknown properties: %s" % ", ".join(unknown_props)) + + @staticmethod + def build(line_str): + """Build an Event instance from a string. + + Parameters + ---------- + line_str : str + Line describing the event. + """ + m = Event._CRE.match(line_str) + assert m is not None + groups = m.groupdict('') + + name = groups["name"] + props = groups["props"].split() + fmt = groups["fmt"] + args = Arguments.build(groups["args"]) + + return Event(name, props, fmt, args) + + def __repr__(self): + """Evaluable string representation for this object.""" + return "Event('%s %s(%s) %s')" % (" ".join(self.properties), + self.name, + self.args, + self.fmt) + +def _read_events(fobj): + res = [] + for line in fobj: + if not line.strip(): + continue + if line.lstrip().startswith('#'): + continue + res.append(Event.build(line)) + return res + + +class TracetoolError (Exception): + """Exception for calls to generate.""" + pass + + +def try_import(mod_name, attr_name = None, attr_default = None): + """Try to import a module and get an attribute from it. + + Parameters + ---------- + mod_name : str + Module name. + attr_name : str, optional + Name of an attribute in the module. + attr_default : optional + Default value if the attribute does not exist in the module. + + Returns + ------- + A pair indicating whether the module could be imported and the module or + object or attribute value. + """ + try: + module = __import__(mod_name, fromlist=["__package__"]) + if attr_name is None: + return True, module + return True, getattr(module, str(attr_name), attr_default) + except ImportError: + return False, None + + +def generate(fevents, format, backend, + binary = None, probe_prefix = None): + """Generate the output for the given (format, backend) pair. + + Parameters + ---------- + fevents : file + Event description file. + format : str + Output format name. + backend : str + Output backend name. + binary : str or None + See tracetool.backend.dtrace.BINARY. + probe_prefix : str or None + See tracetool.backend.dtrace.PROBEPREFIX. + """ + # fix strange python error (UnboundLocalError tracetool) + import tracetool + + format = str(format) + if len(format) is 0: + raise TracetoolError("format not set") + mformat = format.replace("-", "_") + if not tracetool.format.exists(mformat): + raise TracetoolError("unknown format: %s" % format) + + backend = str(backend) + if len(backend) is 0: + raise TracetoolError("backend not set") + mbackend = backend.replace("-", "_") + if not tracetool.backend.exists(mbackend): + raise TracetoolError("unknown backend: %s" % backend) + + if not tracetool.backend.compatible(mbackend, mformat): + raise TracetoolError("backend '%s' not compatible with format '%s'" % + (backend, format)) + + import tracetool.backend.dtrace + tracetool.backend.dtrace.BINARY = binary + tracetool.backend.dtrace.PROBEPREFIX = probe_prefix + + events = _read_events(fevents) + + if backend == "nop": + ( e.properies.add("disable") for e in events ) + + tracetool.format.generate_begin(mformat, events) + tracetool.backend.generate("nop", format, + [ e + for e in events + if "disable" in e.properties ]) + tracetool.backend.generate(backend, format, + [ e + for e in events + if "disable" not in e.properties ]) + tracetool.format.generate_end(mformat, events) diff --git a/scripts/tracetool/backend/__init__.py b/scripts/tracetool/backend/__init__.py new file mode 100644 index 0000000..34b7ed8 --- /dev/null +++ b/scripts/tracetool/backend/__init__.py @@ -0,0 +1,111 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Backend management. + + +Creating new backends +--------------------- + +A new backend named 'foo-bar' corresponds to Python module +'tracetool/backend/foo_bar.py'. + +A backend module should provide a docstring, whose first non-empty line will be +considered its short description. + +All backends must generate their contents through the 'tracetool.out' routine. + + +Backend functions +----------------- + +======== ======================================================================= +Function Description +======== ======================================================================= +<format> Called to generate the format- and backend-specific code for each of + the specified events. If the function does not exist, the backend is + considered not compatible with the given format. +======== ======================================================================= +""" + +__author__ = "LluÃs Vilanova <vilanova@ac.upc.edu>" +__copyright__ = "Copyright 2012, LluÃs Vilanova <vilanova@ac.upc.edu>" +__license__ = "GPL version 2 or (at your option) any later version" + +__maintainer__ = "Stefan Hajnoczi" +__email__ = "stefanha@linux.vnet.ibm.com" + + +import pkgutil + +import tracetool + + +def get_list(): + """Get a list of (name, description) pairs.""" + res = [("nop", "Tracing disabled.")] + for _, modname, _ in pkgutil.iter_modules(tracetool.backend.__path__): + module = tracetool.try_import("tracetool.backend." + modname) + + # just in case; should never fail unless non-module files are put there + if not module[0]: + continue + module = module[1] + + doc = module.__doc__ + if doc is None: + doc = "" + doc = doc.strip().split("\n")[0] + + name = modname.replace("_", "-") + res.append((name, doc)) + return res + + +def exists(name): + """Return whether the given backend exists.""" + if len(name) == 0: + return False + if name == "nop": + return True + name = name.replace("-", "_") + return tracetool.try_import("tracetool.backend." + name)[1] + + +def compatible(backend, format): + """Whether a backend is compatible with the given format.""" + if not exists(backend): + raise ValueError("unknown backend: %s" % backend) + + backend = backend.replace("-", "_") + format = format.replace("-", "_") + + if backend == "nop": + return True + else: + func = tracetool.try_import("tracetool.backend." + backend, + format, None)[1] + return func is not None + + +def _empty(events): + pass + +def generate(backend, format, events): + """Generate the per-event output for the given (backend, format) pair.""" + if not compatible(backend, format): + raise ValueError("backend '%s' not compatible with format '%s'" % + (backend, format)) + + backend = backend.replace("-", "_") + format = format.replace("-", "_") + + if backend == "nop": + func = tracetool.try_import("tracetool.format." + format, + "nop", _empty)[1] + else: + func = tracetool.try_import("tracetool.backend." + backend, + format, None)[1] + + func(events) diff --git a/scripts/tracetool/backend/dtrace.py b/scripts/tracetool/backend/dtrace.py new file mode 100644 index 0000000..9cab75c --- /dev/null +++ b/scripts/tracetool/backend/dtrace.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +DTrace/SystemTAP backend. +""" + +__author__ = "LluÃs Vilanova <vilanova@ac.upc.edu>" +__copyright__ = "Copyright 2012, LluÃs Vilanova <vilanova@ac.upc.edu>" +__license__ = "GPL version 2 or (at your option) any later version" + +__maintainer__ = "Stefan Hajnoczi" +__email__ = "stefanha@linux.vnet.ibm.com" + + +from tracetool import out + + +PROBEPREFIX = None + +def _probeprefix(): + if PROBEPREFIX is None: + raise ValueError("you must set PROBEPREFIX") + return PROBEPREFIX + + +BINARY = None + +def _binary(): + if BINARY is None: + raise ValueError("you must set BINARY") + return BINARY + + +def c(events): + pass + + +def h(events): + out('#include "trace-dtrace.h"', + '') + + for e in events: + out('static inline void trace_%(name)s(%(args)s) {', + ' QEMU_%(uppername)s(%(argnames)s);', + '}', + name = e.name, + args = e.args, + uppername = e.name.upper(), + argnames = ", ".join(e.args.names()), + ) + + +def d(events): + out('provider qemu {') + + for e in events: + args = str(e.args) + + # DTrace provider syntax expects foo() for empty + # params, not foo(void) + if args == 'void': + args = '' + + # Define prototype for probe arguments + out('', + 'probe %(name)s(%(args)s);', + name = e.name, + args = args, + ) + + out('', + '};') + + +def stap(events): + for e in events: + # Define prototype for probe arguments + out('probe %(probeprefix)s.%(name)s = process("%(binary)s").mark("%(name)s")', + '{', + probeprefix = _probeprefix(), + name = e.name, + binary = _binary(), + ) + + i = 1 + if len(e.args) > 0: + for name in e.args.names(): + # Append underscore to reserved keywords + if name in ('limit', 'in', 'next', 'self'): + name += '_' + out(' %s = $arg%d;' % (name, i)) + i += 1 + + out('}') + + out() diff --git a/scripts/tracetool/backend/simple.py b/scripts/tracetool/backend/simple.py new file mode 100644 index 0000000..fbb5717 --- /dev/null +++ b/scripts/tracetool/backend/simple.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Simple built-in backend. +""" + +__author__ = "LluÃs Vilanova <vilanova@ac.upc.edu>" +__copyright__ = "Copyright 2012, LluÃs Vilanova <vilanova@ac.upc.edu>" +__license__ = "GPL version 2 or (at your option) any later version" + +__maintainer__ = "Stefan Hajnoczi" +__email__ = "stefanha@linux.vnet.ibm.com" + + +from tracetool import out + + +def c(events): + out('#include "trace.h"', + '', + 'TraceEvent trace_list[] = {') + + for e in events: + out('{.tp_name = "%(name)s", .state=0},', + name = e.name, + ) + + out('};') + +def h(events): + out('#include "trace/simple.h"', + '') + + for num, e in enumerate(events): + if len(e.args): + argstr = e.args.names() + arg_prefix = ', (uint64_t)(uintptr_t)' + cast_args = arg_prefix + arg_prefix.join(argstr) + simple_args = (str(num) + cast_args) + else: + simple_args = str(num) + + out('static inline void trace_%(name)s(%(args)s)', + '{', + ' trace%(argc)d(%(trace_args)s);', + '}', + name = e.name, + args = e.args, + argc = len(e.args), + trace_args = simple_args, + ) + + out('#define NR_TRACE_EVENTS %d' % len(events)) + out('extern TraceEvent trace_list[NR_TRACE_EVENTS];') diff --git a/scripts/tracetool/backend/stderr.py b/scripts/tracetool/backend/stderr.py new file mode 100644 index 0000000..917fde7 --- /dev/null +++ b/scripts/tracetool/backend/stderr.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Stderr built-in backend. +""" + +__author__ = "LluÃs Vilanova <vilanova@ac.upc.edu>" +__copyright__ = "Copyright 2012, LluÃs Vilanova <vilanova@ac.upc.edu>" +__license__ = "GPL version 2 or (at your option) any later version" + +__maintainer__ = "Stefan Hajnoczi" +__email__ = "stefanha@linux.vnet.ibm.com" + + +from tracetool import out + + +def c(events): + out('#include "trace.h"', + '', + 'TraceEvent trace_list[] = {') + + for e in events: + out('{.tp_name = "%(name)s", .state=0},', + name = e.name, + ) + + out('};') + +def h(events): + out('#include <stdio.h>', + '#include "trace/stderr.h"', + '', + 'extern TraceEvent trace_list[];') + + for num, e in enumerate(events): + argnames = ", ".join(e.args.names()) + if len(e.args) > 0: + argnames = ", " + argnames + + out('static inline void trace_%(name)s(%(args)s)', + '{', + ' if (trace_list[%(event_num)s].state != 0) {', + ' fprintf(stderr, "%(name)s " %(fmt)s "\\n" %(argnames)s);', + ' }', + '}', + name = e.name, + args = e.args, + event_num = num, + fmt = e.fmt, + argnames = argnames, + ) + + out('', + '#define NR_TRACE_EVENTS %d' % len(events)) diff --git a/scripts/tracetool/backend/ust.py b/scripts/tracetool/backend/ust.py new file mode 100644 index 0000000..31a2ff0 --- /dev/null +++ b/scripts/tracetool/backend/ust.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +LTTng User Space Tracing backend. +""" + +__author__ = "LluÃs Vilanova <vilanova@ac.upc.edu>" +__copyright__ = "Copyright 2012, LluÃs Vilanova <vilanova@ac.upc.edu>" +__license__ = "GPL version 2 or (at your option) any later version" + +__maintainer__ = "Stefan Hajnoczi" +__email__ = "stefanha@linux.vnet.ibm.com" + + +from tracetool import out + + +def c(events): + out('#include <ust/marker.h>', + '#undef mutex_lock', + '#undef mutex_unlock', + '#undef inline', + '#undef wmb', + '#include "trace.h"') + + for e in events: + argnames = ", ".join(e.args.names()) + if len(e.args) > 0: + argnames = ', ' + argnames + + out('DEFINE_TRACE(ust_%(name)s);', + '', + 'static void ust_%(name)s_probe(%(args)s)', + '{', + ' trace_mark(ust, %(name)s, %(fmt)s%(argnames)s);', + '}', + name = e.name, + args = e.args, + fmt = e.fmt, + argnames = argnames, + ) + + else: + out('DEFINE_TRACE(ust_%(name)s);', + '', + 'static void ust_%(name)s_probe(%(args)s)', + '{', + ' trace_mark(ust, %(name)s, UST_MARKER_NOARGS);', + '}', + name = e.name, + args = e.args, + ) + + # register probes + out('', + 'static void __attribute__((constructor)) trace_init(void)', + '{') + + for e in events: + out(' register_trace_ust_%(name)s(ust_%(name)s_probe);', + name = e.name, + ) + + out('}') + + +def h(events): + out('#include <ust/tracepoint.h>', + '#undef mutex_lock', + '#undef mutex_unlock', + '#undef inline', + '#undef wmb') + + for e in events: + if len(e.args) > 0: + out('DECLARE_TRACE(ust_%(name)s, TP_PROTO(%(args)s), TP_ARGS(%(argnames)s));', + '#define trace_%(name)s trace_ust_%(name)s', + name = e.name, + args = e.args, + argnames = ", ".join(e.args.names()), + ) + + else: + out('_DECLARE_TRACEPOINT_NOARGS(ust_%(name)s);', + '#define trace_%(name)s trace_ust_%(name)s', + name = e.name, + ) + + out() diff --git a/scripts/tracetool/format/__init__.py b/scripts/tracetool/format/__init__.py new file mode 100644 index 0000000..0e4baf0 --- /dev/null +++ b/scripts/tracetool/format/__init__.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Format management. + + +Creating new formats +-------------------- + +A new format named 'foo-bar' corresponds to Python module +'tracetool/format/foo_bar.py'. + +A format module should provide a docstring, whose first non-empty line will be +considered its short description. + +All formats must generate their contents through the 'tracetool.out' routine. + + +Format functions +---------------- + +All the following functions are optional, and no output will be generated if +they do not exist. + +======== ======================================================================= +Function Description +======== ======================================================================= +begin Called to generate the format-specific file header. +end Called to generate the format-specific file footer. +nop Called to generate the per-event contents when the event is disabled or + the selected backend is 'nop'. +======== ======================================================================= +""" + +__author__ = "LluÃs Vilanova <vilanova@ac.upc.edu>" +__copyright__ = "Copyright 2012, LluÃs Vilanova <vilanova@ac.upc.edu>" +__license__ = "GPL version 2 or (at your option) any later version" + +__maintainer__ = "Stefan Hajnoczi" +__email__ = "stefanha@linux.vnet.ibm.com" + + +import pkgutil + +import tracetool + + +def get_list(): + """Get a list of (name, description) pairs.""" + res = [] + for _, modname, _ in pkgutil.iter_modules(tracetool.format.__path__): + module = tracetool.try_import("tracetool.format." + modname) + + # just in case; should never fail unless non-module files are put there + if not module[0]: + continue + module = module[1] + + doc = module.__doc__ + if doc is None: + doc = "" + doc = doc.strip().split("\n")[0] + + name = modname.replace("_", "-") + res.append((name, doc)) + return res + + +def exists(name): + """Return whether the given format exists.""" + if len(name) == 0: + return False + name = name.replace("-", "_") + return tracetool.try_import("tracetool.format." + name)[1] + + +def _empty(events): + pass + +def generate_begin(name, events): + """Generate the header of the format-specific file.""" + if not exists(name): + raise ValueError("unknown format: %s" % name) + + name = name.replace("-", "_") + func = tracetool.try_import("tracetool.format." + name, + "begin", _empty)[1] + func(events) + +def generate_end(name, events): + """Generate the footer of the format-specific file.""" + if not exists(name): + raise ValueError("unknown format: %s" % name) + + name = name.replace("-", "_") + func = tracetool.try_import("tracetool.format." + name, + "end", _empty)[1] + func(events) diff --git a/scripts/tracetool/format/c.py b/scripts/tracetool/format/c.py new file mode 100644 index 0000000..35555ae --- /dev/null +++ b/scripts/tracetool/format/c.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Generate .c file. +""" + +__author__ = "LluÃs Vilanova <vilanova@ac.upc.edu>" +__copyright__ = "Copyright 2012, LluÃs Vilanova <vilanova@ac.upc.edu>" +__license__ = "GPL version 2 or (at your option) any later version" + +__maintainer__ = "Stefan Hajnoczi" +__email__ = "stefanha@linux.vnet.ibm.com" + + +from tracetool import out + + +def begin(events): + out('/* This file is autogenerated by tracetool, do not edit. */') diff --git a/scripts/tracetool/format/d.py b/scripts/tracetool/format/d.py new file mode 100644 index 0000000..a2d5947 --- /dev/null +++ b/scripts/tracetool/format/d.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Generate .d file (DTrace only). +""" + +__author__ = "LluÃs Vilanova <vilanova@ac.upc.edu>" +__copyright__ = "Copyright 2012, LluÃs Vilanova <vilanova@ac.upc.edu>" +__license__ = "GPL version 2 or (at your option) any later version" + +__maintainer__ = "Stefan Hajnoczi" +__email__ = "stefanha@linux.vnet.ibm.com" + + +from tracetool import out + + +def begin(events): + out('/* This file is autogenerated by tracetool, do not edit. */') diff --git a/scripts/tracetool/format/h.py b/scripts/tracetool/format/h.py new file mode 100644 index 0000000..6ffb3c2 --- /dev/null +++ b/scripts/tracetool/format/h.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Generate .h file. +""" + +__author__ = "LluÃs Vilanova <vilanova@ac.upc.edu>" +__copyright__ = "Copyright 2012, LluÃs Vilanova <vilanova@ac.upc.edu>" +__license__ = "GPL version 2 or (at your option) any later version" + +__maintainer__ = "Stefan Hajnoczi" +__email__ = "stefanha@linux.vnet.ibm.com" + + +from tracetool import out + + +def begin(events): + out('/* This file is autogenerated by tracetool, do not edit. */', + '', + '#ifndef TRACE_H', + '#define TRACE_H', + '', + '#include "qemu-common.h"') + +def end(events): + for e in events: + if "disable" in e.properties: + enabled = 0 + else: + enabled = 1 + out('#define TRACE_%s_ENABLED %d' % (e.name.upper(), enabled)) + out('', + '#endif /* TRACE_H */') + +def nop(events): + for e in events: + out('', + 'static inline void trace_%(name)s(%(args)s)', + '{', + '}', + name = e.name, + args = e.args, + ) diff --git a/scripts/tracetool/format/stap.py b/scripts/tracetool/format/stap.py new file mode 100644 index 0000000..50a4c69 --- /dev/null +++ b/scripts/tracetool/format/stap.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Generate .stp file (DTrace with SystemTAP only). +""" + +__author__ = "LluÃs Vilanova <vilanova@ac.upc.edu>" +__copyright__ = "Copyright 2012, LluÃs Vilanova <vilanova@ac.upc.edu>" +__license__ = "GPL version 2 or (at your option) any later version" + +__maintainer__ = "Stefan Hajnoczi" +__email__ = "stefanha@linux.vnet.ibm.com" + + +from tracetool import out + + +def begin(events): + out('/* This file is autogenerated by tracetool, do not edit. */') diff --git a/spice-qemu-char.c b/spice-qemu-char.c index 1e735ff..09aa22d 100644 --- a/spice-qemu-char.c +++ b/spice-qemu-char.c @@ -209,7 +209,7 @@ CharDriverState *qemu_chr_open_spice(QemuOpts *opts) } } if (subtype == NULL) { - fprintf(stderr, "spice-qemu-char: unsupported name\n"); + fprintf(stderr, "spice-qemu-char: unsupported name: %s\n", name); print_allowed_subtypes(); return NULL; } diff --git a/tests/.gitignore b/tests/.gitignore new file mode 100644 index 0000000..f9041f3 --- /dev/null +++ b/tests/.gitignore @@ -0,0 +1,13 @@ +check-qdict +check-qfloat +check-qint +check-qjson +check-qlist +check-qstring +test-qapi-types.[ch] +test-qapi-visit.[ch] +test-qmp-commands.h +test-qmp-commands +test-qmp-input-strict +test-qmp-marshal.c +*-test diff --git a/tests/qemu-iotests/005 b/tests/qemu-iotests/005 index 74537db..b7970e3 100755 --- a/tests/qemu-iotests/005 +++ b/tests/qemu-iotests/005 @@ -65,7 +65,7 @@ $QEMU_IO -c "read 1024 4096" $TEST_IMG | _filter_qemu_io echo echo "small write" -$QEMU_IO -c "read 8192 4096" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "write 8192 4096" $TEST_IMG | _filter_qemu_io # success, all done echo "*** done" diff --git a/tests/qemu-iotests/005.out b/tests/qemu-iotests/005.out index 718a185..2d3e7df 100644 --- a/tests/qemu-iotests/005.out +++ b/tests/qemu-iotests/005.out @@ -8,6 +8,6 @@ read 4096/4096 bytes at offset 1024 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) small write -read 4096/4096 bytes at offset 8192 +wrote 4096/4096 bytes at offset 8192 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) *** done diff --git a/tests/qemu-iotests/013.out b/tests/qemu-iotests/013.out index 3073e3f..0d57187 100644 --- a/tests/qemu-iotests/013.out +++ b/tests/qemu-iotests/013.out @@ -1,5 +1,5 @@ QA output created by 013 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 cluster_size=4096 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 Testing empty image At offset 0: diff --git a/tests/qemu-iotests/014.out b/tests/qemu-iotests/014.out index 8045613..0258d75 100644 --- a/tests/qemu-iotests/014.out +++ b/tests/qemu-iotests/014.out @@ -1,5 +1,5 @@ QA output created by 014 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 cluster_size=4096 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 Testing empty image: test2: With offset 0 === Clusters to be compressed [1] diff --git a/tests/qemu-iotests/015.out b/tests/qemu-iotests/015.out index 3ba723d..d4b961c 100644 --- a/tests/qemu-iotests/015.out +++ b/tests/qemu-iotests/015.out @@ -1,7 +1,7 @@ QA output created by 015 creating image -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=37748736 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=37748736 creating first snapshot wrote 37748736/37748736 bytes at offset 0 36 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) diff --git a/tests/qemu-iotests/019.out b/tests/qemu-iotests/019.out index 241d30c..b51224b 100644 --- a/tests/qemu-iotests/019.out +++ b/tests/qemu-iotests/019.out @@ -1,5 +1,5 @@ QA output created by 019 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 cluster_size=65536 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 Filling base image === IO: pattern 42 @@ -269,7 +269,7 @@ qemu-io> wrote 65536/65536 bytes at offset 4296015872 qemu-io> No errors were found on the image. Creating test image with backing file -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 backing_file='TEST_DIR/t.IMGFMT.base' cluster_size=65536 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 backing_file='TEST_DIR/t.IMGFMT.base' Filling test image === IO: pattern 43 diff --git a/tests/qemu-iotests/022.out b/tests/qemu-iotests/022.out index b900c71..aed86d5 100644 --- a/tests/qemu-iotests/022.out +++ b/tests/qemu-iotests/022.out @@ -1,5 +1,5 @@ QA output created by 022 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 cluster_size=4096 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 Testing empty image At offset 10485760: diff --git a/tests/qemu-iotests/023.out b/tests/qemu-iotests/023.out index 138434e..8a7c5b2 100644 --- a/tests/qemu-iotests/023.out +++ b/tests/qemu-iotests/023.out @@ -1,7 +1,7 @@ QA output created by 023 Creating new image; cluster size: 1024 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=8589934592 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=8589934592 Testing empty image At offset 0: @@ -5664,7 +5664,7 @@ qemu-io> read 3072/3072 bytes at offset 4295491072 qemu-io> No errors were found on the image. Creating another new image -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=8589934592 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=8589934592 More complex patterns test2: With offset 0 @@ -5887,7 +5887,7 @@ qemu-io> read 2048/2048 bytes at offset 4295001088 qemu-io> No errors were found on the image. Creating new image; cluster size: 4096 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=8589934592 cluster_size=4096 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=8589934592 Testing empty image At offset 0: @@ -12270,7 +12270,7 @@ qemu-io> read 12288/12288 bytes at offset 4301256704 qemu-io> No errors were found on the image. Creating another new image -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=8589934592 cluster_size=4096 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=8589934592 More complex patterns test2: With offset 0 @@ -12493,7 +12493,7 @@ qemu-io> read 8192/8192 bytes at offset 4295102464 qemu-io> No errors were found on the image. Creating new image; cluster size: 16384 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=8589934592 cluster_size=16384 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=8589934592 Testing empty image At offset 0: @@ -18876,7 +18876,7 @@ qemu-io> read 49152/49152 bytes at offset 4395622400 qemu-io> No errors were found on the image. Creating another new image -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=8589934592 cluster_size=16384 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=8589934592 More complex patterns test2: With offset 0 @@ -19099,7 +19099,7 @@ qemu-io> read 32768/32768 bytes at offset 4295507968 qemu-io> No errors were found on the image. Creating new image; cluster size: 65536 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=8589934592 cluster_size=65536 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=8589934592 Testing empty image At offset 0: @@ -25482,7 +25482,7 @@ qemu-io> read 196608/196608 bytes at offset 5905547264 qemu-io> No errors were found on the image. Creating another new image -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=8589934592 cluster_size=65536 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=8589934592 More complex patterns test2: With offset 0 diff --git a/tests/qemu-iotests/024.out b/tests/qemu-iotests/024.out index 4cee216..072207c 100644 --- a/tests/qemu-iotests/024.out +++ b/tests/qemu-iotests/024.out @@ -1,7 +1,7 @@ QA output created by 024 Creating backing file -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=65536 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 === IO: pattern 0x11 qemu-io> wrote 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -21,7 +21,7 @@ qemu-io> wrote 65536/65536 bytes at offset 917504 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) qemu-io> Creating new backing file -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=65536 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 === IO: pattern 0x22 qemu-io> wrote 131072/131072 bytes at offset 0 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -33,7 +33,7 @@ qemu-io> wrote 131072/131072 bytes at offset 786432 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) qemu-io> Creating COW image -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 backing_file='TEST_DIR/t.IMGFMT.base_old' cluster_size=65536 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 backing_file='TEST_DIR/t.IMGFMT.base_old' === IO: pattern 0x33 qemu-io> wrote 262144/262144 bytes at offset 0 256 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) diff --git a/tests/qemu-iotests/026.out b/tests/qemu-iotests/026.out index b503cf2..fb4f20e 100644 --- a/tests/qemu-iotests/026.out +++ b/tests/qemu-iotests/026.out @@ -1,63 +1,63 @@ QA output created by 026 Errors while writing 128 kB -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_update; errno: 5; imm: off; once: on; write write failed: Input/output error 1 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_update; errno: 5; imm: off; once: on; write -b write failed: Input/output error 1 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_update; errno: 5; imm: off; once: off; write write failed: Input/output error 1 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_update; errno: 5; imm: off; once: off; write -b write failed: Input/output error 1 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_update; errno: 28; imm: off; once: on; write write failed: No space left on device 1 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_update; errno: 28; imm: off; once: on; write -b write failed: No space left on device 1 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_update; errno: 28; imm: off; once: off; write write failed: No space left on device 1 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_update; errno: 28; imm: off; once: off; write -b write failed: No space left on device 1 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_load; errno: 5; imm: off; once: on; write wrote 131072/131072 bytes at offset 0 @@ -65,7 +65,7 @@ wrote 131072/131072 bytes at offset 0 write failed: Input/output error read failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_load; errno: 5; imm: off; once: on; write -b wrote 131072/131072 bytes at offset 0 @@ -73,7 +73,7 @@ wrote 131072/131072 bytes at offset 0 write failed: Input/output error read failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_load; errno: 5; imm: off; once: off; write wrote 131072/131072 bytes at offset 0 @@ -81,7 +81,7 @@ wrote 131072/131072 bytes at offset 0 write failed: Input/output error read failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_load; errno: 5; imm: off; once: off; write -b wrote 131072/131072 bytes at offset 0 @@ -89,7 +89,7 @@ wrote 131072/131072 bytes at offset 0 write failed: Input/output error read failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_load; errno: 28; imm: off; once: on; write wrote 131072/131072 bytes at offset 0 @@ -97,7 +97,7 @@ wrote 131072/131072 bytes at offset 0 write failed: No space left on device read failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_load; errno: 28; imm: off; once: on; write -b wrote 131072/131072 bytes at offset 0 @@ -105,7 +105,7 @@ wrote 131072/131072 bytes at offset 0 write failed: No space left on device read failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_load; errno: 28; imm: off; once: off; write wrote 131072/131072 bytes at offset 0 @@ -113,7 +113,7 @@ wrote 131072/131072 bytes at offset 0 write failed: No space left on device read failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_load; errno: 28; imm: off; once: off; write -b wrote 131072/131072 bytes at offset 0 @@ -121,306 +121,306 @@ wrote 131072/131072 bytes at offset 0 write failed: No space left on device read failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_update; errno: 5; imm: off; once: on; write write failed: Input/output error 128 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_update; errno: 5; imm: off; once: on; write -b write failed: Input/output error 128 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_update; errno: 5; imm: off; once: off; write write failed: Input/output error 128 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_update; errno: 5; imm: off; once: off; write -b write failed: Input/output error 128 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_update; errno: 28; imm: off; once: on; write write failed: No space left on device 128 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_update; errno: 28; imm: off; once: on; write -b write failed: No space left on device 128 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_update; errno: 28; imm: off; once: off; write write failed: No space left on device 128 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_update; errno: 28; imm: off; once: off; write -b write failed: No space left on device 128 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_alloc.write; errno: 5; imm: off; once: on; write write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_alloc.write; errno: 5; imm: off; once: on; write -b write failed: Input/output error 1 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_alloc.write; errno: 5; imm: off; once: off; write write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_alloc.write; errno: 5; imm: off; once: off; write -b write failed: Input/output error 1 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_alloc.write; errno: 28; imm: off; once: on; write write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_alloc.write; errno: 28; imm: off; once: on; write -b write failed: No space left on device 1 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_alloc.write; errno: 28; imm: off; once: off; write write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_alloc.write; errno: 28; imm: off; once: off; write -b write failed: No space left on device 1 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: write_aio; errno: 5; imm: off; once: on; write write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: write_aio; errno: 5; imm: off; once: on; write -b write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: write_aio; errno: 5; imm: off; once: off; write write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: write_aio; errno: 5; imm: off; once: off; write -b write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: write_aio; errno: 28; imm: off; once: on; write write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: write_aio; errno: 28; imm: off; once: on; write -b write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: write_aio; errno: 28; imm: off; once: off; write write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: write_aio; errno: 28; imm: off; once: off; write -b write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_load; errno: 5; imm: off; once: on; write write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_load; errno: 5; imm: off; once: on; write -b write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_load; errno: 5; imm: off; once: off; write write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_load; errno: 5; imm: off; once: off; write -b write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_load; errno: 28; imm: off; once: on; write write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_load; errno: 28; imm: off; once: on; write -b write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_load; errno: 28; imm: off; once: off; write write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_load; errno: 28; imm: off; once: off; write -b write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_update_part; errno: 5; imm: off; once: on; write write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_update_part; errno: 5; imm: off; once: on; write -b write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_update_part; errno: 5; imm: off; once: off; write write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_update_part; errno: 5; imm: off; once: off; write -b write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_update_part; errno: 28; imm: off; once: on; write write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_update_part; errno: 28; imm: off; once: on; write -b write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_update_part; errno: 28; imm: off; once: off; write write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_update_part; errno: 28; imm: off; once: off; write -b write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc; errno: 5; imm: off; once: on; write write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc; errno: 5; imm: off; once: on; write -b write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc; errno: 5; imm: off; once: off; write write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc; errno: 5; imm: off; once: off; write -b write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc; errno: 28; imm: off; once: on; write write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc; errno: 28; imm: off; once: on; write -b write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc; errno: 28; imm: off; once: off; write write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc; errno: 28; imm: off; once: off; write -b write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: cluster_alloc; errno: 5; imm: off; once: on; write write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: cluster_alloc; errno: 5; imm: off; once: on; write -b write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: cluster_alloc; errno: 5; imm: off; once: off; write write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: cluster_alloc; errno: 5; imm: off; once: off; write -b write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: cluster_alloc; errno: 28; imm: off; once: on; write write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: cluster_alloc; errno: 28; imm: off; once: on; write -b write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: cluster_alloc; errno: 28; imm: off; once: off; write write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: cluster_alloc; errno: 28; imm: off; once: off; write -b write failed: No space left on device @@ -428,116 +428,116 @@ No errors were found on the image. === Refcout table growth tests === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=512 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc.hookup; errno: 28; imm: off; once: on; write write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=512 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc.hookup; errno: 28; imm: off; once: on; write -b write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=512 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc.hookup; errno: 28; imm: off; once: off; write write failed: No space left on device 55 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=512 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc.hookup; errno: 28; imm: off; once: off; write -b write failed: No space left on device 251 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=512 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc.write; errno: 28; imm: off; once: on; write write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=512 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc.write; errno: 28; imm: off; once: on; write -b write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=512 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc.write; errno: 28; imm: off; once: off; write write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=512 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc.write; errno: 28; imm: off; once: off; write -b write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=512 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc.write_blocks; errno: 28; imm: off; once: on; write write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=512 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc.write_blocks; errno: 28; imm: off; once: on; write -b write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=512 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc.write_blocks; errno: 28; imm: off; once: off; write write failed: No space left on device 10 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=512 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc.write_blocks; errno: 28; imm: off; once: off; write -b write failed: No space left on device 23 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=512 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc.write_table; errno: 28; imm: off; once: on; write write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=512 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc.write_table; errno: 28; imm: off; once: on; write -b write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=512 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc.write_table; errno: 28; imm: off; once: off; write write failed: No space left on device 10 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=512 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc.write_table; errno: 28; imm: off; once: off; write -b write failed: No space left on device 23 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=512 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc.switch_table; errno: 28; imm: off; once: on; write write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=512 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc.switch_table; errno: 28; imm: off; once: on; write -b write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=512 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc.switch_table; errno: 28; imm: off; once: off; write write failed: No space left on device 10 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=512 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc.switch_table; errno: 28; imm: off; once: off; write -b write failed: No space left on device @@ -547,54 +547,54 @@ This means waste of disk space, but no harm to data. === L1 growth tests === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_grow.alloc_table; errno: 5; imm: off; once: on write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_grow.alloc_table; errno: 5; imm: off; once: off write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_grow.alloc_table; errno: 28; imm: off; once: on write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_grow.alloc_table; errno: 28; imm: off; once: off write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_grow.write_table; errno: 5; imm: off; once: on write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_grow.write_table; errno: 5; imm: off; once: off qcow2_free_clusters failed: Input/output error write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_grow.write_table; errno: 28; imm: off; once: on write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_grow.write_table; errno: 28; imm: off; once: off qcow2_free_clusters failed: No space left on device write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_grow.activate_table; errno: 5; imm: off; once: on write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_grow.activate_table; errno: 5; imm: off; once: off qcow2_free_clusters failed: Input/output error @@ -602,12 +602,12 @@ write failed: Input/output error 96 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_grow.activate_table; errno: 28; imm: off; once: on write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_grow.activate_table; errno: 28; imm: off; once: off qcow2_free_clusters failed: No space left on device diff --git a/tests/qemu-iotests/029.out b/tests/qemu-iotests/029.out index c2ad30c..0eedb3a 100644 --- a/tests/qemu-iotests/029.out +++ b/tests/qemu-iotests/029.out @@ -1,9 +1,9 @@ QA output created by 029 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 cluster_size=65536 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 wrote 4096/4096 bytes at offset 0 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=16777216 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=16777216 wrote 4194304/4194304 bytes at offset 0 4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) No errors were found on the image. diff --git a/tests/qemu-iotests/031 b/tests/qemu-iotests/031 index 6365f28..2d5e3b1 100755 --- a/tests/qemu-iotests/031 +++ b/tests/qemu-iotests/031 @@ -45,26 +45,34 @@ _supported_proto generic _supported_os Linux CLUSTER_SIZE=65536 -echo -echo === Create image with unknown header extension === -echo -_make_test_img 64M -./qcow2.py $TEST_IMG add-header-ext 0x12345678 "This is a test header extension" -./qcow2.py $TEST_IMG dump-header -_check_test_img -echo -echo === Rewrite header with no backing file === -echo -$QEMU_IMG rebase -u -b "" $TEST_IMG -./qcow2.py $TEST_IMG dump-header -_check_test_img +# qcow2.py output depends on the exact options used, so override the command +# line here as an exception +for IMGOPTS in "compat=0.10" "compat=1.1"; do -echo -echo === Add a backing file and format === -echo -$QEMU_IMG rebase -u -b "/some/backing/file/path" -F host_device $TEST_IMG -./qcow2.py $TEST_IMG dump-header + echo + echo ===== Testing with -o $IMGOPTS ===== + echo + echo === Create image with unknown header extension === + echo + _make_test_img 64M + ./qcow2.py $TEST_IMG add-header-ext 0x12345678 "This is a test header extension" + ./qcow2.py $TEST_IMG dump-header + _check_test_img + + echo + echo === Rewrite header with no backing file === + echo + $QEMU_IMG rebase -u -b "" $TEST_IMG + ./qcow2.py $TEST_IMG dump-header + _check_test_img + + echo + echo === Add a backing file and format === + echo + $QEMU_IMG rebase -u -b "/some/backing/file/path" -F host_device $TEST_IMG + ./qcow2.py $TEST_IMG dump-header +done # success, all done echo "*** done" diff --git a/tests/qemu-iotests/031.out b/tests/qemu-iotests/031.out index 0f1bf68..d3cab30 100644 --- a/tests/qemu-iotests/031.out +++ b/tests/qemu-iotests/031.out @@ -1,8 +1,10 @@ QA output created by 031 +===== Testing with -o compat=0.10 ===== + === Create image with unknown header extension === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 cluster_size=65536 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 magic 0x514649fb version 2 backing_file_offset 0x0 @@ -16,6 +18,11 @@ refcount_table_offset 0x10000 refcount_table_clusters 1 nb_snapshots 0 snapshot_offset 0x0 +incompatible_features 0x0 +compatible_features 0x0 +autoclear_features 0x0 +refcount_order 4 +header_length 72 Header extension: magic 0x12345678 @@ -39,6 +46,16 @@ refcount_table_offset 0x10000 refcount_table_clusters 1 nb_snapshots 0 snapshot_offset 0x0 +incompatible_features 0x0 +compatible_features 0x0 +autoclear_features 0x0 +refcount_order 4 +header_length 72 + +Header extension: +magic 0x6803f857 +length 0 +data '' Header extension: magic 0x12345678 @@ -51,7 +68,7 @@ No errors were found on the image. magic 0x514649fb version 2 -backing_file_offset 0x90 +backing_file_offset 0x98 backing_file_size 0x17 cluster_bits 16 size 67108864 @@ -62,6 +79,11 @@ refcount_table_offset 0x10000 refcount_table_clusters 1 nb_snapshots 0 snapshot_offset 0x0 +incompatible_features 0x0 +compatible_features 0x0 +autoclear_features 0x0 +refcount_order 4 +header_length 72 Header extension: magic 0xe2792aca @@ -69,6 +91,112 @@ length 11 data 'host_device' Header extension: +magic 0x6803f857 +length 0 +data '' + +Header extension: +magic 0x12345678 +length 31 +data 'This is a test header extension' + + +===== Testing with -o compat=1.1 ===== + +=== Create image with unknown header extension === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +magic 0x514649fb +version 3 +backing_file_offset 0x0 +backing_file_size 0x0 +cluster_bits 16 +size 67108864 +crypt_method 0 +l1_size 1 +l1_table_offset 0x30000 +refcount_table_offset 0x10000 +refcount_table_clusters 1 +nb_snapshots 0 +snapshot_offset 0x0 +incompatible_features 0x0 +compatible_features 0x0 +autoclear_features 0x0 +refcount_order 4 +header_length 104 + +Header extension: +magic 0x12345678 +length 31 +data 'This is a test header extension' + +No errors were found on the image. + +=== Rewrite header with no backing file === + +magic 0x514649fb +version 3 +backing_file_offset 0x0 +backing_file_size 0x0 +cluster_bits 16 +size 67108864 +crypt_method 0 +l1_size 1 +l1_table_offset 0x30000 +refcount_table_offset 0x10000 +refcount_table_clusters 1 +nb_snapshots 0 +snapshot_offset 0x0 +incompatible_features 0x0 +compatible_features 0x0 +autoclear_features 0x0 +refcount_order 4 +header_length 104 + +Header extension: +magic 0x6803f857 +length 0 +data '' + +Header extension: +magic 0x12345678 +length 31 +data 'This is a test header extension' + +No errors were found on the image. + +=== Add a backing file and format === + +magic 0x514649fb +version 3 +backing_file_offset 0xb8 +backing_file_size 0x17 +cluster_bits 16 +size 67108864 +crypt_method 0 +l1_size 1 +l1_table_offset 0x30000 +refcount_table_offset 0x10000 +refcount_table_clusters 1 +nb_snapshots 0 +snapshot_offset 0x0 +incompatible_features 0x0 +compatible_features 0x0 +autoclear_features 0x0 +refcount_order 4 +header_length 104 + +Header extension: +magic 0xe2792aca +length 11 +data 'host_device' + +Header extension: +magic 0x6803f857 +length 0 +data '' + +Header extension: magic 0x12345678 length 31 data 'This is a test header extension' diff --git a/tests/qemu-iotests/032 b/tests/qemu-iotests/032 new file mode 100755 index 0000000..7155568 --- /dev/null +++ b/tests/qemu-iotests/032 @@ -0,0 +1,69 @@ +#!/bin/bash +# +# Test that AIO requests are drained before an image is closed. This used +# to segfault because the request coroutine kept running even after the +# BlockDriverState was freed. +# +# Copyright (C) 2011 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=kwolf@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +here=`pwd` +tmp=/tmp/$$ +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter +. ./common.pattern + +# This works for any image format (though unlikely to segfault for raw) +_supported_fmt generic +_supported_proto generic +_supported_os Linux + +echo +echo === Prepare image === +echo + +CLUSTER_SIZE=65536 +_make_test_img 64M + +# Allocate every other cluster so that afterwards a big write request will +# actually loop a while and issue many I/O requests for the lower layer +for i in $(seq 0 128 4096); do echo "write ${i}k 64k"; done | $QEMU_IO $TEST_IMG | _filter_qemu_io + +echo +echo === AIO request during close === +echo +$QEMU_IO -c "aio_write 0 4M" -c "close" $TEST_IMG | _filter_qemu_io +_check_test_img + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/032.out b/tests/qemu-iotests/032.out new file mode 100644 index 0000000..7272ac2 --- /dev/null +++ b/tests/qemu-iotests/032.out @@ -0,0 +1,78 @@ +QA output created by 032 + +=== Prepare image === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +qemu-io> wrote 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 131072 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 262144 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 393216 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 524288 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 655360 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 786432 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 917504 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 1048576 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 1179648 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 1310720 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 1441792 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 1572864 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 1703936 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 1835008 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 1966080 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 2097152 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 2228224 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 2359296 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 2490368 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 2621440 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 2752512 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 2883584 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 3014656 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 3145728 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 3276800 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 3407872 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 3538944 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 3670016 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 3801088 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 3932160 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 4063232 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 4194304 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> +=== AIO request during close === + +wrote 4194304/4194304 bytes at offset 0 +4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +No errors were found on the image. +*** done diff --git a/tests/qemu-iotests/033 b/tests/qemu-iotests/033 new file mode 100755 index 0000000..9aee078 --- /dev/null +++ b/tests/qemu-iotests/033 @@ -0,0 +1,73 @@ +#!/bin/bash +# +# Test aligned and misaligned write zeroes operations. +# +# Copyright (C) 2012 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=pbonzini@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +here=`pwd` +tmp=/tmp/$$ +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt generic +_supported_proto generic +_supported_os Linux + + +size=128M +_make_test_img $size + +echo +echo "== preparing image ==" +$QEMU_IO -c "write -P 0xa 0x200 0x400" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "write -P 0xa 0x20000 0x600" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "write -z 0x400 0x20000" $TEST_IMG | _filter_qemu_io + +echo +echo "== verifying patterns (1) ==" +$QEMU_IO -c "read -P 0xa 0x200 0x200" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "read -P 0x0 0x400 0x20000" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "read -P 0xa 0x20400 0x200" $TEST_IMG | _filter_qemu_io + +echo +echo "== rewriting zeroes ==" +$QEMU_IO -c "write -P 0xb 0x10000 0x10000" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "write -z 0x10000 0x10000" $TEST_IMG | _filter_qemu_io + +echo +echo "== verifying patterns (2) ==" +$QEMU_IO -c "read -P 0x0 0x400 0x20000" $TEST_IMG | _filter_qemu_io + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/033.out b/tests/qemu-iotests/033.out new file mode 100644 index 0000000..8934883 --- /dev/null +++ b/tests/qemu-iotests/033.out @@ -0,0 +1,29 @@ +QA output created by 033 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 + +== preparing image == +wrote 1024/1024 bytes at offset 512 +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 1536/1536 bytes at offset 131072 +2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 131072/131072 bytes at offset 1024 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== verifying patterns (1) == +read 512/512 bytes at offset 512 +512.000000 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 131072/131072 bytes at offset 1024 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 512/512 bytes at offset 132096 +512.000000 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== rewriting zeroes == +wrote 65536/65536 bytes at offset 65536 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 65536/65536 bytes at offset 65536 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== verifying patterns (2) == +read 131072/131072 bytes at offset 1024 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +*** done diff --git a/tests/qemu-iotests/034 b/tests/qemu-iotests/034 new file mode 100755 index 0000000..8254df8 --- /dev/null +++ b/tests/qemu-iotests/034 @@ -0,0 +1,113 @@ +#!/bin/bash +# +# Test bdrv_write_zeroes with backing files +# +# Copyright (C) 2012 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=kwolf@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +here=`pwd` +tmp=/tmp/$$ +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt qcow qcow2 vmdk qed +_supported_proto generic +_supported_os Linux + +CLUSTER_SIZE=4k +size=128M + +echo +echo "== creating backing file for COW tests ==" + +_make_test_img $size +$QEMU_IO -c "write -P 0x55 0 1M" $TEST_IMG | _filter_qemu_io +mv $TEST_IMG $TEST_IMG.base + +_make_test_img -b $TEST_IMG.base 6G + +echo +echo "== zero write with backing file ==" +$QEMU_IO -c "write -z 64k 192k" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "write -z 513k 13k" $TEST_IMG | _filter_qemu_io + +_check_test_img + +echo +echo "== verifying patterns (3) ==" +$QEMU_IO -c "read -P 0x55 0 64k" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "read -P 0x0 64k 192k" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "read -P 0x55 256k 257k" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "read -P 0x0 513k 13k" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "read -P 0x55 526k 498k" $TEST_IMG | _filter_qemu_io + +echo +echo "== overwriting zero cluster ==" +$QEMU_IO -c "write -P 0xa 60k 8k" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "write -P 0xb 64k 8k" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "write -P 0xc 76k 4k" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "write -P 0xd 252k 8k" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "write -P 0xe 248k 8k" $TEST_IMG | _filter_qemu_io + +_check_test_img + +echo +echo "== verifying patterns (4) ==" +$QEMU_IO -c "read -P 0x55 0 60k" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "read -P 0xa 60k 4k" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "read -P 0xb 64k 8k" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "read -P 0x0 72k 4k" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "read -P 0xc 76k 4k" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "read -P 0x0 80k 168k" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "read -P 0xe 248k 8k" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "read -P 0xd 256k 4k" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "read -P 0x55 260k 64k" $TEST_IMG | _filter_qemu_io + +echo +echo "== re-zeroing overwritten area ==" +$QEMU_IO -c "write -z 64k 192k" $TEST_IMG | _filter_qemu_io + +_check_test_img + +echo +echo "== verifying patterns (5) ==" +$QEMU_IO -c "read -P 0x55 0 60k" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "read -P 0xa 60k 4k" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "read -P 0x0 64k 192k" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "read -P 0xd 256k 4k" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "read -P 0x55 260k 253k" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "read -P 0x0 513k 13k" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "read -P 0x55 526k 498k" $TEST_IMG | _filter_qemu_io + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/034.out b/tests/qemu-iotests/034.out new file mode 100644 index 0000000..e82dae5 --- /dev/null +++ b/tests/qemu-iotests/034.out @@ -0,0 +1,81 @@ +QA output created by 034 + +== creating backing file for COW tests == +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +wrote 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 backing_file='TEST_DIR/t.IMGFMT.base' + +== zero write with backing file == +wrote 196608/196608 bytes at offset 65536 +192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 13312/13312 bytes at offset 525312 +13 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +No errors were found on the image. + +== verifying patterns (3) == +read 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 196608/196608 bytes at offset 65536 +192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 263168/263168 bytes at offset 262144 +257 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 13312/13312 bytes at offset 525312 +13 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 509952/509952 bytes at offset 538624 +498 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== overwriting zero cluster == +wrote 8192/8192 bytes at offset 61440 +8 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 8192/8192 bytes at offset 65536 +8 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 4096/4096 bytes at offset 77824 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 8192/8192 bytes at offset 258048 +8 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 8192/8192 bytes at offset 253952 +8 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +No errors were found on the image. + +== verifying patterns (4) == +read 61440/61440 bytes at offset 0 +60 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 4096/4096 bytes at offset 61440 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 8192/8192 bytes at offset 65536 +8 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 4096/4096 bytes at offset 73728 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 4096/4096 bytes at offset 77824 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 172032/172032 bytes at offset 81920 +168 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 8192/8192 bytes at offset 253952 +8 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 4096/4096 bytes at offset 262144 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 65536/65536 bytes at offset 266240 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== re-zeroing overwritten area == +wrote 196608/196608 bytes at offset 65536 +192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +No errors were found on the image. + +== verifying patterns (5) == +read 61440/61440 bytes at offset 0 +60 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 4096/4096 bytes at offset 61440 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 196608/196608 bytes at offset 65536 +192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 4096/4096 bytes at offset 262144 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 259072/259072 bytes at offset 266240 +253 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 13312/13312 bytes at offset 525312 +13 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 509952/509952 bytes at offset 538624 +498 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +*** done diff --git a/tests/qemu-iotests/check b/tests/qemu-iotests/check index aae1378..432732c 100755 --- a/tests/qemu-iotests/check +++ b/tests/qemu-iotests/check @@ -41,9 +41,6 @@ then exit 1 fi -# we need common -. ./common - # we need common.rc if ! . ./common.rc then @@ -51,6 +48,9 @@ then exit 1 fi +# we need common +. ./common + #if [ `id -u` -ne 0 ] #then # echo "check: QA must be run as root" diff --git a/tests/qemu-iotests/common b/tests/qemu-iotests/common index c187f6c..eeb70cb 100644 --- a/tests/qemu-iotests/common +++ b/tests/qemu-iotests/common @@ -35,6 +35,7 @@ diff="diff -u" verbose=false group=false xgroup=false +imgopts=false showme=false sortme=false expunge=true @@ -44,6 +45,7 @@ rm -f $tmp.list $tmp.tmp $tmp.sed export IMGFMT=raw export IMGPROTO=file +export IMGOPTS="" export QEMU_IO_OPTIONS="" for r @@ -103,6 +105,13 @@ s/ .*//p mv $tmp.tmp $tmp.list xgroup=false continue + + elif $imgopts + then + IMGOPTS="$r" + imgopts=false + continue + fi xpand=true @@ -130,6 +139,7 @@ check options -nocache use O_DIRECT on backing file -misalign misalign memory allocations -n show me, do not run tests + -o options -o options to pass to qemu-img create/convert -T output timestamps -r randomize test order @@ -223,6 +233,10 @@ testlist options showme=true xpand=false ;; + -o) + imgopts=true + xpand=false + ;; -r) # randomize test order randomize=true xpand=false @@ -299,6 +313,9 @@ BEGIN { for (t='$start'; t<='$end'; t++) printf "%03d\n",t }' \ done +# Set default options for qemu-img create -o if they were not specified +_set_default_imgopts + if [ -s $tmp.list ] then # found some valid test numbers ... this is good diff --git a/tests/qemu-iotests/common.config b/tests/qemu-iotests/common.config index a220684..df082e7 100644 --- a/tests/qemu-iotests/common.config +++ b/tests/qemu-iotests/common.config @@ -36,7 +36,7 @@ export LANG=C PATH=".:$PATH" -HOST=`hostname -s` +HOST=`hostname -s 2> /dev/null` HOSTOS=`uname -s` EMAIL=root@localhost # where auto-qa will send its status messages diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc index 4cb8dae..4bc7420 100644 --- a/tests/qemu-iotests/common.rc +++ b/tests/qemu-iotests/common.rc @@ -53,21 +53,44 @@ else TEST_IMG=$IMGPROTO:$TEST_DIR/t.$IMGFMT fi +_optstr_add() +{ + if [ -n "$1" ]; then + echo "$1,$2" + else + echo "$2" + fi +} + +_set_default_imgopts() +{ + if [ "$IMGFMT" == "qcow2" ] && ! (echo "$IMGOPTS" | grep "compat=" > /dev/null); then + IMGOPTS=$(_optstr_add "$IMGOPTS" "compat=1.1") + fi +} + _make_test_img() { # extra qemu-img options can be added by tests # at least one argument (the image size) needs to be added local extra_img_options="" - local cluster_size_filter="s# cluster_size=[0-9]\\+##g" local image_size=$* + local optstr="" + + if [ -n "$IMGOPTS" ]; then + optstr=$(_optstr_add "$optstr" "$IMGOPTS") + fi if [ "$1" = "-b" ]; then extra_img_options="$1 $2" image_size=$3 fi if [ \( "$IMGFMT" = "qcow2" -o "$IMGFMT" = "qed" \) -a -n "$CLUSTER_SIZE" ]; then - extra_img_options="-o cluster_size=$CLUSTER_SIZE $extra_img_options" - cluster_size_filter="" + optstr=$(_optstr_add "$optstr" "cluster_size=$CLUSTER_SIZE") + fi + + if [ -n "$optstr" ]; then + extra_img_options="-o $optstr $extra_img_options" fi # XXX(hch): have global image options? @@ -76,8 +99,9 @@ _make_test_img() sed -e "s#$TEST_DIR#TEST_DIR#g" | \ sed -e "s#$IMGFMT#IMGFMT#g" | \ sed -e "s# encryption=off##g" | \ - sed -e "$cluster_size_filter" | \ + sed -e "s# cluster_size=[0-9]\\+##g" | \ sed -e "s# table_size=0##g" | \ + sed -e "s# compat='[^']*'##g" | \ sed -e "s# compat6=off##g" | \ sed -e "s# static=off##g" } @@ -270,7 +294,11 @@ _require_command() _full_imgfmt_details() { - echo "$IMGFMT" + if [ -n "$IMGOPTS" ]; then + echo "$IMGFMT ($IMGOPTS)" + else + echo "$IMGFMT" + fi } _full_imgproto_details() diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index 1742ede..5934829 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -38,3 +38,6 @@ 029 rw auto quick 030 rw auto 031 rw auto quick +032 rw auto +033 rw auto +034 rw auto backing diff --git a/tests/qemu-iotests/qcow2.py b/tests/qemu-iotests/qcow2.py index bfb47e8..e27196a 100755 --- a/tests/qemu-iotests/qcow2.py +++ b/tests/qemu-iotests/qcow2.py @@ -35,6 +35,13 @@ class QcowHeader: [ uint32_t, '%d', 'refcount_table_clusters' ], [ uint32_t, '%d', 'nb_snapshots' ], [ uint64_t, '%#x', 'snapshot_offset' ], + + # Version 3 header fields + [ uint64_t, '%#x', 'incompatible_features' ], + [ uint64_t, '%#x', 'compatible_features' ], + [ uint64_t, '%#x', 'autoclear_features' ], + [ uint32_t, '%d', 'refcount_order' ], + [ uint32_t, '%d', 'header_length' ], ]; fmt = '>' + ''.join(field[0] for field in fields) @@ -50,9 +57,10 @@ class QcowHeader: self.__dict__ = dict((field[2], header[i]) for i, field in enumerate(QcowHeader.fields)) + self.set_defaults() self.cluster_size = 1 << self.cluster_bits - fd.seek(self.get_header_length()) + fd.seek(self.header_length) self.load_extensions(fd) if self.backing_file_offset: @@ -61,11 +69,13 @@ class QcowHeader: else: self.backing_file = None - def get_header_length(self): + def set_defaults(self): if self.version == 2: - return 72 - else: - raise Exception("version != 2 not supported") + self.incompatible_features = 0 + self.compatible_features = 0 + self.autoclear_features = 0 + self.refcount_order = 4 + self.header_length = 72 def load_extensions(self, fd): self.extensions = [] @@ -86,7 +96,7 @@ class QcowHeader: def update_extensions(self, fd): - fd.seek(self.get_header_length()) + fd.seek(self.header_length) extensions = self.extensions extensions.append(QcowHeaderExtension(0, 0, "")) for ex in extensions: @@ -103,7 +113,7 @@ class QcowHeader: def update(self, fd): - header_bytes = self.get_header_length() + header_bytes = self.header_length self.update_extensions(fd) @@ -129,7 +129,7 @@ void xen_piix_pci_write_config_client(uint32_t address, uint32_t val, int len) void xen_hvm_inject_msi(uint64_t addr, uint32_t data) { - xc_hvm_inject_msi(xen_xc, xen_domid, addr, data); + xen_xc_hvm_inject_msi(xen_xc, xen_domid, addr, data); } static void xen_suspend_notifier(Notifier *notifier, void *data) |