Block pull request

-----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQEcBAABAgAGBQJTupIoAAoJEJykq7OBq3PIwWMIAJQ91tz0Rs29maKfU08hXL47
 2lOOtU53Y8q65ZRCa4x/9RVybXsemrGiIm4vB5zztOOXQiLjYsUYOBPV9FzN3cgX
 /gZQXWCRQ0TpISoLOmACn4KlW8A90acULYYaJ7B3N7x4SgaRd+Np+O/IhdgA++mo
 tQ+/uTBGpbQXH5xSK8H+4+AwvVRgDbLhGqy6ZmZmyE/KqqYNf6Y7Y04n0lLGzV6c
 UrI4K57sTYyOQJY4XCAPTwIvEdQ10usGyRcEQPvdP7zkBIz5TKkPUtKUAPuFJhij
 hO+bF+nRgS/4gQr2teY9DLIiEZEhs7hgQnW1O7ByZlBBH/spmh19Mh0G6PUpB2w=
 =fF3v
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/stefanha/tags/block-pull-request' into staging

Block pull request

# gpg: Signature made Mon 07 Jul 2014 13:27:20 BST using RSA key ID 81AB73C8
# gpg: Good signature from "Stefan Hajnoczi <stefanha@redhat.com>"
# gpg:                 aka "Stefan Hajnoczi <stefanha@gmail.com>"

* remotes/stefanha/tags/block-pull-request:
  qmp: show QOM properties in device-list-properties
  dataplane: submit I/O as a batch
  linux-aio: implement io plug, unplug and flush io queue
  block: block: introduce APIs for submitting IO as a batch
  ahci: map memory via device's address space instead of address_space_memory
  raw-posix: Fix raw_getlength() to always return -errno on error
  qemu-iotests: Disable Quorum testing in 041 when Quorum is not builtin
  ahci.c: mask unused flags when reading size PRDT DBC
  MAINTAINERS: add Stefan Hajnoczi to IDE maintainers
  mirror: Fix qiov size for short requests
  Fix nocow typos in manpage

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2014-07-07 15:02:36 +01:00
commit 9540d1f8d9
16 changed files with 357 additions and 54 deletions

View File

@ -563,6 +563,7 @@ Devices
------- -------
IDE IDE
M: Kevin Wolf <kwolf@redhat.com> M: Kevin Wolf <kwolf@redhat.com>
M: Stefan Hajnoczi <stefanha@redhat.com>
S: Odd Fixes S: Odd Fixes
F: include/hw/ide.h F: include/hw/ide.h
F: hw/ide/ F: hw/ide/

31
block.c
View File

@ -1905,6 +1905,7 @@ void bdrv_drain_all(void)
bool bs_busy; bool bs_busy;
aio_context_acquire(aio_context); aio_context_acquire(aio_context);
bdrv_flush_io_queue(bs);
bdrv_start_throttled_reqs(bs); bdrv_start_throttled_reqs(bs);
bs_busy = bdrv_requests_pending(bs); bs_busy = bdrv_requests_pending(bs);
bs_busy |= aio_poll(aio_context, bs_busy); bs_busy |= aio_poll(aio_context, bs_busy);
@ -5782,3 +5783,33 @@ BlockDriverState *check_to_replace_node(const char *node_name, Error **errp)
return to_replace_bs; return to_replace_bs;
} }
void bdrv_io_plug(BlockDriverState *bs)
{
BlockDriver *drv = bs->drv;
if (drv && drv->bdrv_io_plug) {
drv->bdrv_io_plug(bs);
} else if (bs->file) {
bdrv_io_plug(bs->file);
}
}
void bdrv_io_unplug(BlockDriverState *bs)
{
BlockDriver *drv = bs->drv;
if (drv && drv->bdrv_io_unplug) {
drv->bdrv_io_unplug(bs);
} else if (bs->file) {
bdrv_io_unplug(bs->file);
}
}
void bdrv_flush_io_queue(BlockDriverState *bs)
{
BlockDriver *drv = bs->drv;
if (drv && drv->bdrv_flush_io_queue) {
drv->bdrv_flush_io_queue(bs);
} else if (bs->file) {
bdrv_flush_io_queue(bs->file);
}
}

View File

@ -25,6 +25,8 @@
*/ */
#define MAX_EVENTS 128 #define MAX_EVENTS 128
#define MAX_QUEUED_IO 128
struct qemu_laiocb { struct qemu_laiocb {
BlockDriverAIOCB common; BlockDriverAIOCB common;
struct qemu_laio_state *ctx; struct qemu_laio_state *ctx;
@ -36,9 +38,19 @@ struct qemu_laiocb {
QLIST_ENTRY(qemu_laiocb) node; QLIST_ENTRY(qemu_laiocb) node;
}; };
typedef struct {
struct iocb *iocbs[MAX_QUEUED_IO];
int plugged;
unsigned int size;
unsigned int idx;
} LaioQueue;
struct qemu_laio_state { struct qemu_laio_state {
io_context_t ctx; io_context_t ctx;
EventNotifier e; EventNotifier e;
/* io queue for submit at batch */
LaioQueue io_q;
}; };
static inline ssize_t io_event_ret(struct io_event *ev) static inline ssize_t io_event_ret(struct io_event *ev)
@ -135,6 +147,79 @@ static const AIOCBInfo laio_aiocb_info = {
.cancel = laio_cancel, .cancel = laio_cancel,
}; };
static void ioq_init(LaioQueue *io_q)
{
io_q->size = MAX_QUEUED_IO;
io_q->idx = 0;
io_q->plugged = 0;
}
static int ioq_submit(struct qemu_laio_state *s)
{
int ret, i = 0;
int len = s->io_q.idx;
do {
ret = io_submit(s->ctx, len, s->io_q.iocbs);
} while (i++ < 3 && ret == -EAGAIN);
/* empty io queue */
s->io_q.idx = 0;
if (ret < 0) {
i = 0;
} else {
i = ret;
}
for (; i < len; i++) {
struct qemu_laiocb *laiocb =
container_of(s->io_q.iocbs[i], struct qemu_laiocb, iocb);
laiocb->ret = (ret < 0) ? ret : -EIO;
qemu_laio_process_completion(s, laiocb);
}
return ret;
}
static void ioq_enqueue(struct qemu_laio_state *s, struct iocb *iocb)
{
unsigned int idx = s->io_q.idx;
s->io_q.iocbs[idx++] = iocb;
s->io_q.idx = idx;
/* submit immediately if queue is full */
if (idx == s->io_q.size) {
ioq_submit(s);
}
}
void laio_io_plug(BlockDriverState *bs, void *aio_ctx)
{
struct qemu_laio_state *s = aio_ctx;
s->io_q.plugged++;
}
int laio_io_unplug(BlockDriverState *bs, void *aio_ctx, bool unplug)
{
struct qemu_laio_state *s = aio_ctx;
int ret = 0;
assert(s->io_q.plugged > 0 || !unplug);
if (unplug && --s->io_q.plugged > 0) {
return 0;
}
if (s->io_q.idx > 0) {
ret = ioq_submit(s);
}
return ret;
}
BlockDriverAIOCB *laio_submit(BlockDriverState *bs, void *aio_ctx, int fd, BlockDriverAIOCB *laio_submit(BlockDriverState *bs, void *aio_ctx, int fd,
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
BlockDriverCompletionFunc *cb, void *opaque, int type) BlockDriverCompletionFunc *cb, void *opaque, int type)
@ -168,8 +253,13 @@ BlockDriverAIOCB *laio_submit(BlockDriverState *bs, void *aio_ctx, int fd,
} }
io_set_eventfd(&laiocb->iocb, event_notifier_get_fd(&s->e)); io_set_eventfd(&laiocb->iocb, event_notifier_get_fd(&s->e));
if (io_submit(s->ctx, 1, &iocbs) < 0) if (!s->io_q.plugged) {
goto out_free_aiocb; if (io_submit(s->ctx, 1, &iocbs) < 0) {
goto out_free_aiocb;
}
} else {
ioq_enqueue(s, iocbs);
}
return &laiocb->common; return &laiocb->common;
out_free_aiocb: out_free_aiocb:
@ -204,6 +294,8 @@ void *laio_init(void)
goto out_close_efd; goto out_close_efd;
} }
ioq_init(&s->io_q);
return s; return s;
out_close_efd: out_close_efd:

View File

@ -265,9 +265,11 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
next_sector = sector_num; next_sector = sector_num;
while (nb_chunks-- > 0) { while (nb_chunks-- > 0) {
MirrorBuffer *buf = QSIMPLEQ_FIRST(&s->buf_free); MirrorBuffer *buf = QSIMPLEQ_FIRST(&s->buf_free);
size_t remaining = (nb_sectors * BDRV_SECTOR_SIZE) - op->qiov.size;
QSIMPLEQ_REMOVE_HEAD(&s->buf_free, next); QSIMPLEQ_REMOVE_HEAD(&s->buf_free, next);
s->buf_free_count--; s->buf_free_count--;
qemu_iovec_add(&op->qiov, buf, s->granularity); qemu_iovec_add(&op->qiov, buf, MIN(s->granularity, remaining));
/* Advance the HBitmapIter in parallel, so that we do not examine /* Advance the HBitmapIter in parallel, so that we do not examine
* the same sector twice. * the same sector twice.

View File

@ -40,6 +40,8 @@ BlockDriverAIOCB *laio_submit(BlockDriverState *bs, void *aio_ctx, int fd,
BlockDriverCompletionFunc *cb, void *opaque, int type); BlockDriverCompletionFunc *cb, void *opaque, int type);
void laio_detach_aio_context(void *s, AioContext *old_context); void laio_detach_aio_context(void *s, AioContext *old_context);
void laio_attach_aio_context(void *s, AioContext *new_context); void laio_attach_aio_context(void *s, AioContext *new_context);
void laio_io_plug(BlockDriverState *bs, void *aio_ctx);
int laio_io_unplug(BlockDriverState *bs, void *aio_ctx, bool unplug);
#endif #endif
#ifdef _WIN32 #ifdef _WIN32

View File

@ -1057,6 +1057,36 @@ static BlockDriverAIOCB *raw_aio_submit(BlockDriverState *bs,
cb, opaque, type); cb, opaque, type);
} }
static void raw_aio_plug(BlockDriverState *bs)
{
#ifdef CONFIG_LINUX_AIO
BDRVRawState *s = bs->opaque;
if (s->use_aio) {
laio_io_plug(bs, s->aio_ctx);
}
#endif
}
static void raw_aio_unplug(BlockDriverState *bs)
{
#ifdef CONFIG_LINUX_AIO
BDRVRawState *s = bs->opaque;
if (s->use_aio) {
laio_io_unplug(bs, s->aio_ctx, true);
}
#endif
}
static void raw_aio_flush_io_queue(BlockDriverState *bs)
{
#ifdef CONFIG_LINUX_AIO
BDRVRawState *s = bs->opaque;
if (s->use_aio) {
laio_io_unplug(bs, s->aio_ctx, false);
}
#endif
}
static BlockDriverAIOCB *raw_aio_readv(BlockDriverState *bs, static BlockDriverAIOCB *raw_aio_readv(BlockDriverState *bs,
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
BlockDriverCompletionFunc *cb, void *opaque) BlockDriverCompletionFunc *cb, void *opaque)
@ -1133,12 +1163,12 @@ static int64_t raw_getlength(BlockDriverState *bs)
struct stat st; struct stat st;
if (fstat(fd, &st)) if (fstat(fd, &st))
return -1; return -errno;
if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) { if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) {
struct disklabel dl; struct disklabel dl;
if (ioctl(fd, DIOCGDINFO, &dl)) if (ioctl(fd, DIOCGDINFO, &dl))
return -1; return -errno;
return (uint64_t)dl.d_secsize * return (uint64_t)dl.d_secsize *
dl.d_partitions[DISKPART(st.st_rdev)].p_size; dl.d_partitions[DISKPART(st.st_rdev)].p_size;
} else } else
@ -1152,7 +1182,7 @@ static int64_t raw_getlength(BlockDriverState *bs)
struct stat st; struct stat st;
if (fstat(fd, &st)) if (fstat(fd, &st))
return -1; return -errno;
if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) { if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) {
struct dkwedge_info dkw; struct dkwedge_info dkw;
@ -1162,7 +1192,7 @@ static int64_t raw_getlength(BlockDriverState *bs)
struct disklabel dl; struct disklabel dl;
if (ioctl(fd, DIOCGDINFO, &dl)) if (ioctl(fd, DIOCGDINFO, &dl))
return -1; return -errno;
return (uint64_t)dl.d_secsize * return (uint64_t)dl.d_secsize *
dl.d_partitions[DISKPART(st.st_rdev)].p_size; dl.d_partitions[DISKPART(st.st_rdev)].p_size;
} }
@ -1175,6 +1205,7 @@ static int64_t raw_getlength(BlockDriverState *bs)
BDRVRawState *s = bs->opaque; BDRVRawState *s = bs->opaque;
struct dk_minfo minfo; struct dk_minfo minfo;
int ret; int ret;
int64_t size;
ret = fd_open(bs); ret = fd_open(bs);
if (ret < 0) { if (ret < 0) {
@ -1193,7 +1224,11 @@ static int64_t raw_getlength(BlockDriverState *bs)
* There are reports that lseek on some devices fails, but * There are reports that lseek on some devices fails, but
* irc discussion said that contingency on contingency was overkill. * irc discussion said that contingency on contingency was overkill.
*/ */
return lseek(s->fd, 0, SEEK_END); size = lseek(s->fd, 0, SEEK_END);
if (size < 0) {
return -errno;
}
return size;
} }
#elif defined(CONFIG_BSD) #elif defined(CONFIG_BSD)
static int64_t raw_getlength(BlockDriverState *bs) static int64_t raw_getlength(BlockDriverState *bs)
@ -1231,6 +1266,9 @@ again:
size = LLONG_MAX; size = LLONG_MAX;
#else #else
size = lseek(fd, 0LL, SEEK_END); size = lseek(fd, 0LL, SEEK_END);
if (size < 0) {
return -errno;
}
#endif #endif
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
switch(s->type) { switch(s->type) {
@ -1247,6 +1285,9 @@ again:
#endif #endif
} else { } else {
size = lseek(fd, 0, SEEK_END); size = lseek(fd, 0, SEEK_END);
if (size < 0) {
return -errno;
}
} }
return size; return size;
} }
@ -1255,13 +1296,18 @@ static int64_t raw_getlength(BlockDriverState *bs)
{ {
BDRVRawState *s = bs->opaque; BDRVRawState *s = bs->opaque;
int ret; int ret;
int64_t size;
ret = fd_open(bs); ret = fd_open(bs);
if (ret < 0) { if (ret < 0) {
return ret; return ret;
} }
return lseek(s->fd, 0, SEEK_END); size = lseek(s->fd, 0, SEEK_END);
if (size < 0) {
return -errno;
}
return size;
} }
#endif #endif
@ -1528,6 +1574,9 @@ static BlockDriver bdrv_file = {
.bdrv_aio_flush = raw_aio_flush, .bdrv_aio_flush = raw_aio_flush,
.bdrv_aio_discard = raw_aio_discard, .bdrv_aio_discard = raw_aio_discard,
.bdrv_refresh_limits = raw_refresh_limits, .bdrv_refresh_limits = raw_refresh_limits,
.bdrv_io_plug = raw_aio_plug,
.bdrv_io_unplug = raw_aio_unplug,
.bdrv_flush_io_queue = raw_aio_flush_io_queue,
.bdrv_truncate = raw_truncate, .bdrv_truncate = raw_truncate,
.bdrv_getlength = raw_getlength, .bdrv_getlength = raw_getlength,
@ -1927,6 +1976,9 @@ static BlockDriver bdrv_host_device = {
.bdrv_aio_flush = raw_aio_flush, .bdrv_aio_flush = raw_aio_flush,
.bdrv_aio_discard = hdev_aio_discard, .bdrv_aio_discard = hdev_aio_discard,
.bdrv_refresh_limits = raw_refresh_limits, .bdrv_refresh_limits = raw_refresh_limits,
.bdrv_io_plug = raw_aio_plug,
.bdrv_io_unplug = raw_aio_unplug,
.bdrv_flush_io_queue = raw_aio_flush_io_queue,
.bdrv_truncate = raw_truncate, .bdrv_truncate = raw_truncate,
.bdrv_getlength = raw_getlength, .bdrv_getlength = raw_getlength,
@ -2072,6 +2124,9 @@ static BlockDriver bdrv_host_floppy = {
.bdrv_aio_writev = raw_aio_writev, .bdrv_aio_writev = raw_aio_writev,
.bdrv_aio_flush = raw_aio_flush, .bdrv_aio_flush = raw_aio_flush,
.bdrv_refresh_limits = raw_refresh_limits, .bdrv_refresh_limits = raw_refresh_limits,
.bdrv_io_plug = raw_aio_plug,
.bdrv_io_unplug = raw_aio_unplug,
.bdrv_flush_io_queue = raw_aio_flush_io_queue,
.bdrv_truncate = raw_truncate, .bdrv_truncate = raw_truncate,
.bdrv_getlength = raw_getlength, .bdrv_getlength = raw_getlength,
@ -2200,6 +2255,9 @@ static BlockDriver bdrv_host_cdrom = {
.bdrv_aio_writev = raw_aio_writev, .bdrv_aio_writev = raw_aio_writev,
.bdrv_aio_flush = raw_aio_flush, .bdrv_aio_flush = raw_aio_flush,
.bdrv_refresh_limits = raw_refresh_limits, .bdrv_refresh_limits = raw_refresh_limits,
.bdrv_io_plug = raw_aio_plug,
.bdrv_io_unplug = raw_aio_unplug,
.bdrv_flush_io_queue = raw_aio_flush_io_queue,
.bdrv_truncate = raw_truncate, .bdrv_truncate = raw_truncate,
.bdrv_getlength = raw_getlength, .bdrv_getlength = raw_getlength,
@ -2334,6 +2392,9 @@ static BlockDriver bdrv_host_cdrom = {
.bdrv_aio_writev = raw_aio_writev, .bdrv_aio_writev = raw_aio_writev,
.bdrv_aio_flush = raw_aio_flush, .bdrv_aio_flush = raw_aio_flush,
.bdrv_refresh_limits = raw_refresh_limits, .bdrv_refresh_limits = raw_refresh_limits,
.bdrv_io_plug = raw_aio_plug,
.bdrv_io_unplug = raw_aio_unplug,
.bdrv_flush_io_queue = raw_aio_flush_io_queue,
.bdrv_truncate = raw_truncate, .bdrv_truncate = raw_truncate,
.bdrv_getlength = raw_getlength, .bdrv_getlength = raw_getlength,

View File

@ -84,6 +84,7 @@ static void handle_notify(EventNotifier *e)
}; };
event_notifier_test_and_clear(&s->host_notifier); event_notifier_test_and_clear(&s->host_notifier);
bdrv_io_plug(s->blk->conf.bs);
for (;;) { for (;;) {
/* Disable guest->host notifies to avoid unnecessary vmexits */ /* Disable guest->host notifies to avoid unnecessary vmexits */
vring_disable_notification(s->vdev, &s->vring); vring_disable_notification(s->vdev, &s->vring);
@ -117,6 +118,7 @@ static void handle_notify(EventNotifier *e)
break; break;
} }
} }
bdrv_io_unplug(s->blk->conf.bs);
} }
/* Context: QEMU global mutex held */ /* Context: QEMU global mutex held */

View File

@ -175,17 +175,18 @@ static void ahci_trigger_irq(AHCIState *s, AHCIDevice *d,
ahci_check_irq(s); ahci_check_irq(s);
} }
static void map_page(uint8_t **ptr, uint64_t addr, uint32_t wanted) static void map_page(AddressSpace *as, uint8_t **ptr, uint64_t addr,
uint32_t wanted)
{ {
hwaddr len = wanted; hwaddr len = wanted;
if (*ptr) { if (*ptr) {
cpu_physical_memory_unmap(*ptr, len, 1, len); dma_memory_unmap(as, *ptr, len, DMA_DIRECTION_FROM_DEVICE, len);
} }
*ptr = cpu_physical_memory_map(addr, &len, 1); *ptr = dma_memory_map(as, addr, &len, DMA_DIRECTION_FROM_DEVICE);
if (len < wanted) { if (len < wanted) {
cpu_physical_memory_unmap(*ptr, len, 1, len); dma_memory_unmap(as, *ptr, len, DMA_DIRECTION_FROM_DEVICE, len);
*ptr = NULL; *ptr = NULL;
} }
} }
@ -198,24 +199,24 @@ static void ahci_port_write(AHCIState *s, int port, int offset, uint32_t val)
switch (offset) { switch (offset) {
case PORT_LST_ADDR: case PORT_LST_ADDR:
pr->lst_addr = val; pr->lst_addr = val;
map_page(&s->dev[port].lst, map_page(s->as, &s->dev[port].lst,
((uint64_t)pr->lst_addr_hi << 32) | pr->lst_addr, 1024); ((uint64_t)pr->lst_addr_hi << 32) | pr->lst_addr, 1024);
s->dev[port].cur_cmd = NULL; s->dev[port].cur_cmd = NULL;
break; break;
case PORT_LST_ADDR_HI: case PORT_LST_ADDR_HI:
pr->lst_addr_hi = val; pr->lst_addr_hi = val;
map_page(&s->dev[port].lst, map_page(s->as, &s->dev[port].lst,
((uint64_t)pr->lst_addr_hi << 32) | pr->lst_addr, 1024); ((uint64_t)pr->lst_addr_hi << 32) | pr->lst_addr, 1024);
s->dev[port].cur_cmd = NULL; s->dev[port].cur_cmd = NULL;
break; break;
case PORT_FIS_ADDR: case PORT_FIS_ADDR:
pr->fis_addr = val; pr->fis_addr = val;
map_page(&s->dev[port].res_fis, map_page(s->as, &s->dev[port].res_fis,
((uint64_t)pr->fis_addr_hi << 32) | pr->fis_addr, 256); ((uint64_t)pr->fis_addr_hi << 32) | pr->fis_addr, 256);
break; break;
case PORT_FIS_ADDR_HI: case PORT_FIS_ADDR_HI:
pr->fis_addr_hi = val; pr->fis_addr_hi = val;
map_page(&s->dev[port].res_fis, map_page(s->as, &s->dev[port].res_fis,
((uint64_t)pr->fis_addr_hi << 32) | pr->fis_addr, 256); ((uint64_t)pr->fis_addr_hi << 32) | pr->fis_addr, 256);
break; break;
case PORT_IRQ_STAT: case PORT_IRQ_STAT:
@ -639,6 +640,11 @@ static void ahci_write_fis_d2h(AHCIDevice *ad, uint8_t *cmd_fis)
} }
} }
static int prdt_tbl_entry_size(const AHCI_SG *tbl)
{
return (le32_to_cpu(tbl->flags_size) & AHCI_PRDT_SIZE_MASK) + 1;
}
static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist, int offset) static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist, int offset)
{ {
AHCICmdHdr *cmd = ad->cur_cmd; AHCICmdHdr *cmd = ad->cur_cmd;
@ -681,7 +687,7 @@ static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist, int offset)
sum = 0; sum = 0;
for (i = 0; i < sglist_alloc_hint; i++) { for (i = 0; i < sglist_alloc_hint; i++) {
/* flags_size is zero-based */ /* flags_size is zero-based */
tbl_entry_size = (le32_to_cpu(tbl[i].flags_size) + 1); tbl_entry_size = prdt_tbl_entry_size(&tbl[i]);
if (offset <= (sum + tbl_entry_size)) { if (offset <= (sum + tbl_entry_size)) {
off_idx = i; off_idx = i;
off_pos = offset - sum; off_pos = offset - sum;
@ -700,12 +706,12 @@ static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist, int offset)
qemu_sglist_init(sglist, qbus->parent, (sglist_alloc_hint - off_idx), qemu_sglist_init(sglist, qbus->parent, (sglist_alloc_hint - off_idx),
ad->hba->as); ad->hba->as);
qemu_sglist_add(sglist, le64_to_cpu(tbl[off_idx].addr + off_pos), qemu_sglist_add(sglist, le64_to_cpu(tbl[off_idx].addr + off_pos),
le32_to_cpu(tbl[off_idx].flags_size) + 1 - off_pos); prdt_tbl_entry_size(&tbl[off_idx]) - off_pos);
for (i = off_idx + 1; i < sglist_alloc_hint; i++) { for (i = off_idx + 1; i < sglist_alloc_hint; i++) {
/* flags_size is zero-based */ /* flags_size is zero-based */
qemu_sglist_add(sglist, le64_to_cpu(tbl[i].addr), qemu_sglist_add(sglist, le64_to_cpu(tbl[i].addr),
le32_to_cpu(tbl[i].flags_size) + 1); prdt_tbl_entry_size(&tbl[i]));
} }
} }
@ -1260,9 +1266,9 @@ static int ahci_state_post_load(void *opaque, int version_id)
ad = &s->dev[i]; ad = &s->dev[i];
AHCIPortRegs *pr = &ad->port_regs; AHCIPortRegs *pr = &ad->port_regs;
map_page(&ad->lst, map_page(s->as, &ad->lst,
((uint64_t)pr->lst_addr_hi << 32) | pr->lst_addr, 1024); ((uint64_t)pr->lst_addr_hi << 32) | pr->lst_addr, 1024);
map_page(&ad->res_fis, map_page(s->as, &ad->res_fis,
((uint64_t)pr->fis_addr_hi << 32) | pr->fis_addr, 256); ((uint64_t)pr->fis_addr_hi << 32) | pr->fis_addr, 256);
/* /*
* All pending i/o should be flushed out on a migrate. However, * All pending i/o should be flushed out on a migrate. However,

View File

@ -201,6 +201,8 @@
#define AHCI_COMMAND_TABLE_ACMD 0x40 #define AHCI_COMMAND_TABLE_ACMD 0x40
#define AHCI_PRDT_SIZE_MASK 0x3fffff
#define IDE_FEATURE_DMA 1 #define IDE_FEATURE_DMA 1
#define READ_FPDMA_QUEUED 0x60 #define READ_FPDMA_QUEUED 0x60

View File

@ -584,4 +584,8 @@ AioContext *bdrv_get_aio_context(BlockDriverState *bs);
*/ */
void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context); void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context);
void bdrv_io_plug(BlockDriverState *bs);
void bdrv_io_unplug(BlockDriverState *bs);
void bdrv_flush_io_queue(BlockDriverState *bs);
#endif #endif

View File

@ -261,6 +261,11 @@ struct BlockDriver {
void (*bdrv_attach_aio_context)(BlockDriverState *bs, void (*bdrv_attach_aio_context)(BlockDriverState *bs,
AioContext *new_context); AioContext *new_context);
/* io queue for linux-aio */
void (*bdrv_io_plug)(BlockDriverState *bs);
void (*bdrv_io_unplug)(BlockDriverState *bs);
void (*bdrv_flush_io_queue)(BlockDriverState *bs);
QLIST_ENTRY(BlockDriver) list; QLIST_ENTRY(BlockDriver) list;
}; };

View File

@ -590,7 +590,7 @@ check -r all} is required, which may take some time.
This option can only be enabled if @code{compat=1.1} is specified. This option can only be enabled if @code{compat=1.1} is specified.
@item nocow @item nocow
If this option is set to @code{on}, it will trun off COW of the file. It's only If this option is set to @code{on}, it will turn off COW of the file. It's only
valid on btrfs, no effect on other file systems. valid on btrfs, no effect on other file systems.
Btrfs has low performance when hosting a VM image file, even more when the guest Btrfs has low performance when hosting a VM image file, even more when the guest
@ -603,7 +603,7 @@ does.
Note: this option is only valid to new or empty files. If there is an existing Note: this option is only valid to new or empty files. If there is an existing
file which is COW and has data blocks already, it couldn't be changed to NOCOW file which is COW and has data blocks already, it couldn't be changed to NOCOW
by setting @code{nocow=on}. One can issue @code{lsattr filename} to check if by setting @code{nocow=on}. One can issue @code{lsattr filename} to check if
the NOCOW flag is set or not (Capitabl 'C' is NOCOW flag). the NOCOW flag is set or not (Capital 'C' is NOCOW flag).
@end table @end table

View File

@ -475,7 +475,7 @@ check -r all} is required, which may take some time.
This option can only be enabled if @code{compat=1.1} is specified. This option can only be enabled if @code{compat=1.1} is specified.
@item nocow @item nocow
If this option is set to @code{on}, it will trun off COW of the file. It's only If this option is set to @code{on}, it will turn off COW of the file. It's only
valid on btrfs, no effect on other file systems. valid on btrfs, no effect on other file systems.
Btrfs has low performance when hosting a VM image file, even more when the guest Btrfs has low performance when hosting a VM image file, even more when the guest
@ -488,7 +488,7 @@ does.
Note: this option is only valid to new or empty files. If there is an existing Note: this option is only valid to new or empty files. If there is an existing
file which is COW and has data blocks already, it couldn't be changed to NOCOW file which is COW and has data blocks already, it couldn't be changed to NOCOW
by setting @code{nocow=on}. One can issue @code{lsattr filename} to check if by setting @code{nocow=on}. One can issue @code{lsattr filename} to check if
the NOCOW flag is set or not (Capitabl 'C' is NOCOW flag). the NOCOW flag is set or not (Capital 'C' is NOCOW flag).
@end table @end table

101
qmp.c
View File

@ -433,11 +433,57 @@ ObjectTypeInfoList *qmp_qom_list_types(bool has_implements,
return ret; return ret;
} }
/* Return a DevicePropertyInfo for a qdev property.
*
* If a qdev property with the given name does not exist, use the given default
* type. If the qdev property info should not be shown, return NULL.
*
* The caller must free the return value.
*/
static DevicePropertyInfo *make_device_property_info(ObjectClass *klass,
const char *name,
const char *default_type)
{
DevicePropertyInfo *info;
Property *prop;
do {
for (prop = DEVICE_CLASS(klass)->props; prop && prop->name; prop++) {
if (strcmp(name, prop->name) != 0) {
continue;
}
/*
* TODO Properties without a parser are just for dirty hacks.
* qdev_prop_ptr is the only such PropertyInfo. It's marked
* for removal. This conditional should be removed along with
* it.
*/
if (!prop->info->set) {
return NULL; /* no way to set it, don't show */
}
info = g_malloc0(sizeof(*info));
info->name = g_strdup(prop->name);
info->type = g_strdup(prop->info->legacy_name ?: prop->info->name);
return info;
}
klass = object_class_get_parent(klass);
} while (klass != object_class_by_name(TYPE_DEVICE));
/* Not a qdev property, use the default type */
info = g_malloc0(sizeof(*info));
info->name = g_strdup(name);
info->type = g_strdup(default_type);
return info;
}
DevicePropertyInfoList *qmp_device_list_properties(const char *typename, DevicePropertyInfoList *qmp_device_list_properties(const char *typename,
Error **errp) Error **errp)
{ {
ObjectClass *klass; ObjectClass *klass;
Property *prop; Object *obj;
ObjectProperty *prop;
DevicePropertyInfoList *prop_list = NULL; DevicePropertyInfoList *prop_list = NULL;
klass = object_class_by_name(typename); klass = object_class_by_name(typename);
@ -453,32 +499,39 @@ DevicePropertyInfoList *qmp_device_list_properties(const char *typename,
return NULL; return NULL;
} }
do { obj = object_new(typename);
for (prop = DEVICE_CLASS(klass)->props; prop && prop->name; prop++) {
DevicePropertyInfoList *entry;
DevicePropertyInfo *info;
/* QTAILQ_FOREACH(prop, &obj->properties, node) {
* TODO Properties without a parser are just for dirty hacks. DevicePropertyInfo *info;
* qdev_prop_ptr is the only such PropertyInfo. It's marked DevicePropertyInfoList *entry;
* for removal. This conditional should be removed along with
* it.
*/
if (!prop->info->set) {
continue; /* no way to set it, don't show */
}
info = g_malloc0(sizeof(*info)); /* Skip Object and DeviceState properties */
info->name = g_strdup(prop->name); if (strcmp(prop->name, "type") == 0 ||
info->type = g_strdup(prop->info->legacy_name ?: prop->info->name); strcmp(prop->name, "realized") == 0 ||
strcmp(prop->name, "hotpluggable") == 0 ||
entry = g_malloc0(sizeof(*entry)); strcmp(prop->name, "parent_bus") == 0) {
entry->value = info; continue;
entry->next = prop_list;
prop_list = entry;
} }
klass = object_class_get_parent(klass);
} while (klass != object_class_by_name(TYPE_DEVICE)); /* Skip legacy properties since they are just string versions of
* properties that we already list.
*/
if (strstart(prop->name, "legacy-", NULL)) {
continue;
}
info = make_device_property_info(klass, prop->name, prop->type);
if (!info) {
continue;
}
entry = g_malloc0(sizeof(*entry));
entry->value = info;
entry->next = prop_list;
prop_list = entry;
}
object_unref(obj);
return prop_list; return prop_list;
} }

View File

@ -217,6 +217,11 @@ class TestSingleDriveZeroLength(TestSingleDrive):
test_small_buffer2 = None test_small_buffer2 = None
test_large_cluster = None test_large_cluster = None
class TestSingleDriveUnalignedLength(TestSingleDrive):
image_len = 1025 * 1024
test_small_buffer2 = None
test_large_cluster = None
class TestMirrorNoBacking(ImageMirroringTestCase): class TestMirrorNoBacking(ImageMirroringTestCase):
image_len = 2 * 1024 * 1024 # MB image_len = 2 * 1024 * 1024 # MB
@ -735,6 +740,9 @@ class TestRepairQuorum(ImageMirroringTestCase):
image_len = 1 * 1024 * 1024 # MB image_len = 1 * 1024 * 1024 # MB
IMAGES = [ quorum_img1, quorum_img2, quorum_img3 ] IMAGES = [ quorum_img1, quorum_img2, quorum_img3 ]
def has_quorum(self):
return 'quorum' in iotests.qemu_img_pipe('--help')
def setUp(self): def setUp(self):
self.vm = iotests.VM() self.vm = iotests.VM()
@ -752,8 +760,9 @@ class TestRepairQuorum(ImageMirroringTestCase):
#assemble the quorum block device from the individual files #assemble the quorum block device from the individual files
args = { "options" : { "driver": "quorum", "id": "quorum0", args = { "options" : { "driver": "quorum", "id": "quorum0",
"vote-threshold": 2, "children": [ "img0", "img1", "img2" ] } } "vote-threshold": 2, "children": [ "img0", "img1", "img2" ] } }
result = self.vm.qmp("blockdev-add", **args) if self.has_quorum():
self.assert_qmp(result, 'return', {}) result = self.vm.qmp("blockdev-add", **args)
self.assert_qmp(result, 'return', {})
def tearDown(self): def tearDown(self):
@ -766,6 +775,9 @@ class TestRepairQuorum(ImageMirroringTestCase):
pass pass
def test_complete(self): def test_complete(self):
if not self.has_quorum():
return
self.assert_no_active_block_jobs() self.assert_no_active_block_jobs()
result = self.vm.qmp('drive-mirror', device='quorum0', sync='full', result = self.vm.qmp('drive-mirror', device='quorum0', sync='full',
@ -784,6 +796,9 @@ class TestRepairQuorum(ImageMirroringTestCase):
'target image does not match source after mirroring') 'target image does not match source after mirroring')
def test_cancel(self): def test_cancel(self):
if not self.has_quorum():
return
self.assert_no_active_block_jobs() self.assert_no_active_block_jobs()
result = self.vm.qmp('drive-mirror', device='quorum0', sync='full', result = self.vm.qmp('drive-mirror', device='quorum0', sync='full',
@ -800,6 +815,9 @@ class TestRepairQuorum(ImageMirroringTestCase):
self.vm.shutdown() self.vm.shutdown()
def test_cancel_after_ready(self): def test_cancel_after_ready(self):
if not self.has_quorum():
return
self.assert_no_active_block_jobs() self.assert_no_active_block_jobs()
result = self.vm.qmp('drive-mirror', device='quorum0', sync='full', result = self.vm.qmp('drive-mirror', device='quorum0', sync='full',
@ -818,6 +836,9 @@ class TestRepairQuorum(ImageMirroringTestCase):
'target image does not match source after mirroring') 'target image does not match source after mirroring')
def test_pause(self): def test_pause(self):
if not self.has_quorum():
return
self.assert_no_active_block_jobs() self.assert_no_active_block_jobs()
result = self.vm.qmp('drive-mirror', device='quorum0', sync='full', result = self.vm.qmp('drive-mirror', device='quorum0', sync='full',
@ -846,6 +867,9 @@ class TestRepairQuorum(ImageMirroringTestCase):
'target image does not match source after mirroring') 'target image does not match source after mirroring')
def test_medium_not_found(self): def test_medium_not_found(self):
if not self.has_quorum():
return
result = self.vm.qmp('drive-mirror', device='ide1-cd0', sync='full', result = self.vm.qmp('drive-mirror', device='ide1-cd0', sync='full',
node_name='repair0', node_name='repair0',
replaces='img1', replaces='img1',
@ -853,6 +877,9 @@ class TestRepairQuorum(ImageMirroringTestCase):
self.assert_qmp(result, 'error/class', 'GenericError') self.assert_qmp(result, 'error/class', 'GenericError')
def test_image_not_found(self): def test_image_not_found(self):
if not self.has_quorum():
return
result = self.vm.qmp('drive-mirror', device='quorum0', sync='full', result = self.vm.qmp('drive-mirror', device='quorum0', sync='full',
node_name='repair0', node_name='repair0',
replaces='img1', replaces='img1',
@ -861,6 +888,9 @@ class TestRepairQuorum(ImageMirroringTestCase):
self.assert_qmp(result, 'error/class', 'GenericError') self.assert_qmp(result, 'error/class', 'GenericError')
def test_device_not_found(self): def test_device_not_found(self):
if not self.has_quorum():
return
result = self.vm.qmp('drive-mirror', device='nonexistent', sync='full', result = self.vm.qmp('drive-mirror', device='nonexistent', sync='full',
node_name='repair0', node_name='repair0',
replaces='img1', replaces='img1',
@ -868,6 +898,9 @@ class TestRepairQuorum(ImageMirroringTestCase):
self.assert_qmp(result, 'error/class', 'DeviceNotFound') self.assert_qmp(result, 'error/class', 'DeviceNotFound')
def test_wrong_sync_mode(self): def test_wrong_sync_mode(self):
if not self.has_quorum():
return
result = self.vm.qmp('drive-mirror', device='quorum0', result = self.vm.qmp('drive-mirror', device='quorum0',
node_name='repair0', node_name='repair0',
replaces='img1', replaces='img1',
@ -875,12 +908,18 @@ class TestRepairQuorum(ImageMirroringTestCase):
self.assert_qmp(result, 'error/class', 'GenericError') self.assert_qmp(result, 'error/class', 'GenericError')
def test_no_node_name(self): def test_no_node_name(self):
if not self.has_quorum():
return
result = self.vm.qmp('drive-mirror', device='quorum0', sync='full', result = self.vm.qmp('drive-mirror', device='quorum0', sync='full',
replaces='img1', replaces='img1',
target=quorum_repair_img, format=iotests.imgfmt) target=quorum_repair_img, format=iotests.imgfmt)
self.assert_qmp(result, 'error/class', 'GenericError') self.assert_qmp(result, 'error/class', 'GenericError')
def test_unexistant_replaces(self): def test_unexistant_replaces(self):
if not self.has_quorum():
return
result = self.vm.qmp('drive-mirror', device='quorum0', sync='full', result = self.vm.qmp('drive-mirror', device='quorum0', sync='full',
node_name='repair0', node_name='repair0',
replaces='img77', replaces='img77',
@ -888,6 +927,9 @@ class TestRepairQuorum(ImageMirroringTestCase):
self.assert_qmp(result, 'error/class', 'GenericError') self.assert_qmp(result, 'error/class', 'GenericError')
def test_after_a_quorum_snapshot(self): def test_after_a_quorum_snapshot(self):
if not self.has_quorum():
return
result = self.vm.qmp('blockdev-snapshot-sync', node_name='img1', result = self.vm.qmp('blockdev-snapshot-sync', node_name='img1',
snapshot_file=quorum_snapshot_file, snapshot_file=quorum_snapshot_file,
snapshot_node_name="snap1"); snapshot_node_name="snap1");

View File

@ -1,5 +1,5 @@
.............................................. ......................................................
---------------------------------------------------------------------- ----------------------------------------------------------------------
Ran 46 tests Ran 54 tests
OK OK