diff options
Diffstat (limited to 'hw')
| -rw-r--r-- | hw/9pfs/xen-9p-backend.c | 32 | ||||
| -rw-r--r-- | hw/block/xen_disk.c | 614 | ||||
| -rw-r--r-- | hw/char/xen_console.c | 9 | ||||
| -rw-r--r-- | hw/i386/intel_iommu.c | 396 | ||||
| -rw-r--r-- | hw/i386/kvm/clock.c | 2 | ||||
| -rw-r--r-- | hw/i386/trace-events | 5 | ||||
| -rw-r--r-- | hw/i386/xen/xen-hvm.c | 76 | ||||
| -rw-r--r-- | hw/i386/xen/xen_pvdevice.c | 11 | ||||
| -rw-r--r-- | hw/mem/nvdimm.c | 2 | ||||
| -rw-r--r-- | hw/net/xen_nic.c | 33 | ||||
| -rw-r--r-- | hw/pci-host/q35.c | 17 | ||||
| -rw-r--r-- | hw/usb/xen-usb.c | 37 | ||||
| -rw-r--r-- | hw/virtio/trace-events | 1 | ||||
| -rw-r--r-- | hw/virtio/vhost-user.c | 45 | ||||
| -rw-r--r-- | hw/virtio/vhost.c | 7 | ||||
| -rw-r--r-- | hw/virtio/virtio-balloon.c | 2 | ||||
| -rw-r--r-- | hw/virtio/virtio-pci.c | 22 | ||||
| -rw-r--r-- | hw/virtio/virtio.c | 13 | ||||
| -rw-r--r-- | hw/xen/xen_backend.c | 178 | ||||
| -rw-r--r-- | hw/xen/xen_pt.c | 2 | ||||
| -rw-r--r-- | hw/xen/xen_pt_config_init.c | 2 |
21 files changed, 776 insertions, 730 deletions
diff --git a/hw/9pfs/xen-9p-backend.c b/hw/9pfs/xen-9p-backend.c index 95e50c4dfc..6026780f95 100644 --- a/hw/9pfs/xen-9p-backend.c +++ b/hw/9pfs/xen-9p-backend.c @@ -331,14 +331,14 @@ static int xen_9pfs_free(struct XenDevice *xendev) for (i = 0; i < xen_9pdev->num_rings; i++) { if (xen_9pdev->rings[i].data != NULL) { - xengnttab_unmap(xen_9pdev->xendev.gnttabdev, - xen_9pdev->rings[i].data, - (1 << xen_9pdev->rings[i].ring_order)); + xen_be_unmap_grant_refs(&xen_9pdev->xendev, + xen_9pdev->rings[i].data, + (1 << xen_9pdev->rings[i].ring_order)); } if (xen_9pdev->rings[i].intf != NULL) { - xengnttab_unmap(xen_9pdev->xendev.gnttabdev, - xen_9pdev->rings[i].intf, - 1); + xen_be_unmap_grant_refs(&xen_9pdev->xendev, + xen_9pdev->rings[i].intf, + 1); } if (xen_9pdev->rings[i].bh != NULL) { qemu_bh_delete(xen_9pdev->rings[i].bh); @@ -390,11 +390,10 @@ static int xen_9pfs_connect(struct XenDevice *xendev) } g_free(str); - xen_9pdev->rings[i].intf = xengnttab_map_grant_ref( - xen_9pdev->xendev.gnttabdev, - xen_9pdev->xendev.dom, - xen_9pdev->rings[i].ref, - PROT_READ | PROT_WRITE); + xen_9pdev->rings[i].intf = + xen_be_map_grant_ref(&xen_9pdev->xendev, + xen_9pdev->rings[i].ref, + PROT_READ | PROT_WRITE); if (!xen_9pdev->rings[i].intf) { goto out; } @@ -403,12 +402,11 @@ static int xen_9pfs_connect(struct XenDevice *xendev) goto out; } xen_9pdev->rings[i].ring_order = ring_order; - xen_9pdev->rings[i].data = xengnttab_map_domain_grant_refs( - xen_9pdev->xendev.gnttabdev, - (1 << ring_order), - xen_9pdev->xendev.dom, - xen_9pdev->rings[i].intf->ref, - PROT_READ | PROT_WRITE); + xen_9pdev->rings[i].data = + xen_be_map_grant_refs(&xen_9pdev->xendev, + xen_9pdev->rings[i].intf->ref, + (1 << ring_order), + PROT_READ | PROT_WRITE); if (!xen_9pdev->rings[i].data) { goto out; } diff --git a/hw/block/xen_disk.c b/hw/block/xen_disk.c index f74fcd42d1..9fbc0cdb87 100644 --- a/hw/block/xen_disk.c +++ b/hw/block/xen_disk.c @@ -36,27 +36,9 @@ /* ------------------------------------------------------------- */ -static int batch_maps = 0; - -/* ------------------------------------------------------------- */ - #define BLOCK_SIZE 512 #define IOCB_COUNT (BLKIF_MAX_SEGMENTS_PER_REQUEST + 2) -struct PersistentGrant { - void *page; - struct XenBlkDev *blkdev; -}; - -typedef struct PersistentGrant PersistentGrant; - -struct PersistentRegion { - void *addr; - int num; -}; - -typedef struct PersistentRegion PersistentRegion; - struct ioreq { blkif_request_t req; int16_t status; @@ -64,16 +46,9 @@ struct ioreq { /* parsed request */ off_t start; QEMUIOVector v; + void *buf; + size_t size; int presync; - uint8_t mapped; - - /* grant mapping */ - uint32_t domids[BLKIF_MAX_SEGMENTS_PER_REQUEST]; - uint32_t refs[BLKIF_MAX_SEGMENTS_PER_REQUEST]; - int prot; - void *page[BLKIF_MAX_SEGMENTS_PER_REQUEST]; - void *pages; - int num_unmap; /* aio status */ int aio_inflight; @@ -104,7 +79,6 @@ struct XenBlkDev { int protocol; blkif_back_rings_t rings; int more_work; - int cnt_map; /* request lists */ QLIST_HEAD(inflight_head, ioreq) inflight; @@ -115,13 +89,7 @@ struct XenBlkDev { int requests_finished; unsigned int max_requests; - /* Persistent grants extension */ gboolean feature_discard; - gboolean feature_persistent; - GTree *persistent_gnts; - GSList *persistent_regions; - unsigned int persistent_gnt_count; - unsigned int max_grants; /* qemu block driver */ DriveInfo *dinfo; @@ -139,14 +107,9 @@ static void ioreq_reset(struct ioreq *ioreq) memset(&ioreq->req, 0, sizeof(ioreq->req)); ioreq->status = 0; ioreq->start = 0; + ioreq->buf = NULL; + ioreq->size = 0; ioreq->presync = 0; - ioreq->mapped = 0; - - memset(ioreq->domids, 0, sizeof(ioreq->domids)); - memset(ioreq->refs, 0, sizeof(ioreq->refs)); - ioreq->prot = 0; - memset(ioreq->page, 0, sizeof(ioreq->page)); - ioreq->pages = NULL; ioreq->aio_inflight = 0; ioreq->aio_errors = 0; @@ -158,46 +121,6 @@ static void ioreq_reset(struct ioreq *ioreq) qemu_iovec_reset(&ioreq->v); } -static gint int_cmp(gconstpointer a, gconstpointer b, gpointer user_data) -{ - uint ua = GPOINTER_TO_UINT(a); - uint ub = GPOINTER_TO_UINT(b); - return (ua > ub) - (ua < ub); -} - -static void destroy_grant(gpointer pgnt) -{ - PersistentGrant *grant = pgnt; - xengnttab_handle *gnt = grant->blkdev->xendev.gnttabdev; - - if (xengnttab_unmap(gnt, grant->page, 1) != 0) { - xen_pv_printf(&grant->blkdev->xendev, 0, - "xengnttab_unmap failed: %s\n", - strerror(errno)); - } - grant->blkdev->persistent_gnt_count--; - xen_pv_printf(&grant->blkdev->xendev, 3, - "unmapped grant %p\n", grant->page); - g_free(grant); -} - -static void remove_persistent_region(gpointer data, gpointer dev) -{ - PersistentRegion *region = data; - struct XenBlkDev *blkdev = dev; - xengnttab_handle *gnt = blkdev->xendev.gnttabdev; - - if (xengnttab_unmap(gnt, region->addr, region->num) != 0) { - xen_pv_printf(&blkdev->xendev, 0, - "xengnttab_unmap region %p failed: %s\n", - region->addr, strerror(errno)); - } - xen_pv_printf(&blkdev->xendev, 3, - "unmapped grant region %p with %d pages\n", - region->addr, region->num); - g_free(region); -} - static struct ioreq *ioreq_start(struct XenBlkDev *blkdev) { struct ioreq *ioreq = NULL; @@ -210,7 +133,7 @@ static struct ioreq *ioreq_start(struct XenBlkDev *blkdev) ioreq = g_malloc0(sizeof(*ioreq)); ioreq->blkdev = blkdev; blkdev->requests_total++; - qemu_iovec_init(&ioreq->v, BLKIF_MAX_SEGMENTS_PER_REQUEST); + qemu_iovec_init(&ioreq->v, 1); } else { /* get one from freelist */ ioreq = QLIST_FIRST(&blkdev->freelist); @@ -255,17 +178,16 @@ static void ioreq_release(struct ioreq *ioreq, bool finish) static int ioreq_parse(struct ioreq *ioreq) { struct XenBlkDev *blkdev = ioreq->blkdev; - uintptr_t mem; + struct XenDevice *xendev = &blkdev->xendev; size_t len; int i; - xen_pv_printf(&blkdev->xendev, 3, + xen_pv_printf(xendev, 3, "op %d, nr %d, handle %d, id %" PRId64 ", sector %" PRId64 "\n", ioreq->req.operation, ioreq->req.nr_segments, ioreq->req.handle, ioreq->req.id, ioreq->req.sector_number); switch (ioreq->req.operation) { case BLKIF_OP_READ: - ioreq->prot = PROT_WRITE; /* to memory */ break; case BLKIF_OP_FLUSH_DISKCACHE: ioreq->presync = 1; @@ -274,45 +196,40 @@ static int ioreq_parse(struct ioreq *ioreq) } /* fall through */ case BLKIF_OP_WRITE: - ioreq->prot = PROT_READ; /* from memory */ break; case BLKIF_OP_DISCARD: return 0; default: - xen_pv_printf(&blkdev->xendev, 0, "error: unknown operation (%d)\n", + xen_pv_printf(xendev, 0, "error: unknown operation (%d)\n", ioreq->req.operation); goto err; }; if (ioreq->req.operation != BLKIF_OP_READ && blkdev->mode[0] != 'w') { - xen_pv_printf(&blkdev->xendev, 0, "error: write req for ro device\n"); + xen_pv_printf(xendev, 0, "error: write req for ro device\n"); goto err; } ioreq->start = ioreq->req.sector_number * blkdev->file_blk; for (i = 0; i < ioreq->req.nr_segments; i++) { if (i == BLKIF_MAX_SEGMENTS_PER_REQUEST) { - xen_pv_printf(&blkdev->xendev, 0, "error: nr_segments too big\n"); + xen_pv_printf(xendev, 0, "error: nr_segments too big\n"); goto err; } if (ioreq->req.seg[i].first_sect > ioreq->req.seg[i].last_sect) { - xen_pv_printf(&blkdev->xendev, 0, "error: first > last sector\n"); + xen_pv_printf(xendev, 0, "error: first > last sector\n"); goto err; } if (ioreq->req.seg[i].last_sect * BLOCK_SIZE >= XC_PAGE_SIZE) { - xen_pv_printf(&blkdev->xendev, 0, "error: page crossing\n"); + xen_pv_printf(xendev, 0, "error: page crossing\n"); goto err; } - ioreq->domids[i] = blkdev->xendev.dom; - ioreq->refs[i] = ioreq->req.seg[i].gref; - - mem = ioreq->req.seg[i].first_sect * blkdev->file_blk; len = (ioreq->req.seg[i].last_sect - ioreq->req.seg[i].first_sect + 1) * blkdev->file_blk; - qemu_iovec_add(&ioreq->v, (void*)mem, len); + ioreq->size += len; } - if (ioreq->start + ioreq->v.size > blkdev->file_size) { - xen_pv_printf(&blkdev->xendev, 0, "error: access beyond end of file\n"); + if (ioreq->start + ioreq->size > blkdev->file_size) { + xen_pv_printf(xendev, 0, "error: access beyond end of file\n"); goto err; } return 0; @@ -322,279 +239,48 @@ err: return -1; } -static void ioreq_unmap(struct ioreq *ioreq) -{ - xengnttab_handle *gnt = ioreq->blkdev->xendev.gnttabdev; - int i; - - if (ioreq->num_unmap == 0 || ioreq->mapped == 0) { - return; - } - if (batch_maps) { - if (!ioreq->pages) { - return; - } - if (xengnttab_unmap(gnt, ioreq->pages, ioreq->num_unmap) != 0) { - xen_pv_printf(&ioreq->blkdev->xendev, 0, - "xengnttab_unmap failed: %s\n", - strerror(errno)); - } - ioreq->blkdev->cnt_map -= ioreq->num_unmap; - ioreq->pages = NULL; - } else { - for (i = 0; i < ioreq->num_unmap; i++) { - if (!ioreq->page[i]) { - continue; - } - if (xengnttab_unmap(gnt, ioreq->page[i], 1) != 0) { - xen_pv_printf(&ioreq->blkdev->xendev, 0, - "xengnttab_unmap failed: %s\n", - strerror(errno)); - } - ioreq->blkdev->cnt_map--; - ioreq->page[i] = NULL; - } - } - ioreq->mapped = 0; -} - -static int ioreq_map(struct ioreq *ioreq) -{ - xengnttab_handle *gnt = ioreq->blkdev->xendev.gnttabdev; - uint32_t domids[BLKIF_MAX_SEGMENTS_PER_REQUEST]; - uint32_t refs[BLKIF_MAX_SEGMENTS_PER_REQUEST]; - void *page[BLKIF_MAX_SEGMENTS_PER_REQUEST]; - int i, j, new_maps = 0; - PersistentGrant *grant; - PersistentRegion *region; - /* domids and refs variables will contain the information necessary - * to map the grants that are needed to fulfill this request. - * - * After mapping the needed grants, the page array will contain the - * memory address of each granted page in the order specified in ioreq - * (disregarding if it's a persistent grant or not). - */ - - if (ioreq->v.niov == 0 || ioreq->mapped == 1) { - return 0; - } - if (ioreq->blkdev->feature_persistent) { - for (i = 0; i < ioreq->v.niov; i++) { - grant = g_tree_lookup(ioreq->blkdev->persistent_gnts, - GUINT_TO_POINTER(ioreq->refs[i])); - - if (grant != NULL) { - page[i] = grant->page; - xen_pv_printf(&ioreq->blkdev->xendev, 3, - "using persistent-grant %" PRIu32 "\n", - ioreq->refs[i]); - } else { - /* Add the grant to the list of grants that - * should be mapped - */ - domids[new_maps] = ioreq->domids[i]; - refs[new_maps] = ioreq->refs[i]; - page[i] = NULL; - new_maps++; - } - } - /* Set the protection to RW, since grants may be reused later - * with a different protection than the one needed for this request - */ - ioreq->prot = PROT_WRITE | PROT_READ; - } else { - /* All grants in the request should be mapped */ - memcpy(refs, ioreq->refs, sizeof(refs)); - memcpy(domids, ioreq->domids, sizeof(domids)); - memset(page, 0, sizeof(page)); - new_maps = ioreq->v.niov; - } - - if (batch_maps && new_maps) { - ioreq->pages = xengnttab_map_grant_refs - (gnt, new_maps, domids, refs, ioreq->prot); - if (ioreq->pages == NULL) { - xen_pv_printf(&ioreq->blkdev->xendev, 0, - "can't map %d grant refs (%s, %d maps)\n", - new_maps, strerror(errno), ioreq->blkdev->cnt_map); - return -1; - } - for (i = 0, j = 0; i < ioreq->v.niov; i++) { - if (page[i] == NULL) { - page[i] = ioreq->pages + (j++) * XC_PAGE_SIZE; - } - } - ioreq->blkdev->cnt_map += new_maps; - } else if (new_maps) { - for (i = 0; i < new_maps; i++) { - ioreq->page[i] = xengnttab_map_grant_ref - (gnt, domids[i], refs[i], ioreq->prot); - if (ioreq->page[i] == NULL) { - xen_pv_printf(&ioreq->blkdev->xendev, 0, - "can't map grant ref %d (%s, %d maps)\n", - refs[i], strerror(errno), ioreq->blkdev->cnt_map); - ioreq->mapped = 1; - ioreq_unmap(ioreq); - return -1; - } - ioreq->blkdev->cnt_map++; - } - for (i = 0, j = 0; i < ioreq->v.niov; i++) { - if (page[i] == NULL) { - page[i] = ioreq->page[j++]; - } - } - } - if (ioreq->blkdev->feature_persistent && new_maps != 0 && - (!batch_maps || (ioreq->blkdev->persistent_gnt_count + new_maps <= - ioreq->blkdev->max_grants))) { - /* - * If we are using persistent grants and batch mappings only - * add the new maps to the list of persistent grants if the whole - * area can be persistently mapped. - */ - if (batch_maps) { - region = g_malloc0(sizeof(*region)); - region->addr = ioreq->pages; - region->num = new_maps; - ioreq->blkdev->persistent_regions = g_slist_append( - ioreq->blkdev->persistent_regions, - region); - } - while ((ioreq->blkdev->persistent_gnt_count < ioreq->blkdev->max_grants) - && new_maps) { - /* Go through the list of newly mapped grants and add as many - * as possible to the list of persistently mapped grants. - * - * Since we start at the end of ioreq->page(s), we only need - * to decrease new_maps to prevent this granted pages from - * being unmapped in ioreq_unmap. - */ - grant = g_malloc0(sizeof(*grant)); - new_maps--; - if (batch_maps) { - grant->page = ioreq->pages + (new_maps) * XC_PAGE_SIZE; - } else { - grant->page = ioreq->page[new_maps]; - } - grant->blkdev = ioreq->blkdev; - xen_pv_printf(&ioreq->blkdev->xendev, 3, - "adding grant %" PRIu32 " page: %p\n", - refs[new_maps], grant->page); - g_tree_insert(ioreq->blkdev->persistent_gnts, - GUINT_TO_POINTER(refs[new_maps]), - grant); - ioreq->blkdev->persistent_gnt_count++; - } - assert(!batch_maps || new_maps == 0); - } - for (i = 0; i < ioreq->v.niov; i++) { - ioreq->v.iov[i].iov_base += (uintptr_t)page[i]; - } - ioreq->mapped = 1; - ioreq->num_unmap = new_maps; - return 0; -} - -#if CONFIG_XEN_CTRL_INTERFACE_VERSION >= 40800 - -static void ioreq_free_copy_buffers(struct ioreq *ioreq) -{ - int i; - - for (i = 0; i < ioreq->v.niov; i++) { - ioreq->page[i] = NULL; - } - - qemu_vfree(ioreq->pages); -} - -static int ioreq_init_copy_buffers(struct ioreq *ioreq) -{ - int i; - - if (ioreq->v.niov == 0) { - return 0; - } - - ioreq->pages = qemu_memalign(XC_PAGE_SIZE, ioreq->v.niov * XC_PAGE_SIZE); - - for (i = 0; i < ioreq->v.niov; i++) { - ioreq->page[i] = ioreq->pages + i * XC_PAGE_SIZE; - ioreq->v.iov[i].iov_base = ioreq->page[i]; - } - - return 0; -} - static int ioreq_grant_copy(struct ioreq *ioreq) { - xengnttab_handle *gnt = ioreq->blkdev->xendev.gnttabdev; - xengnttab_grant_copy_segment_t segs[BLKIF_MAX_SEGMENTS_PER_REQUEST]; + struct XenBlkDev *blkdev = ioreq->blkdev; + struct XenDevice *xendev = &blkdev->xendev; + XenGrantCopySegment segs[BLKIF_MAX_SEGMENTS_PER_REQUEST]; int i, count, rc; - int64_t file_blk = ioreq->blkdev->file_blk; + int64_t file_blk = blkdev->file_blk; + bool to_domain = (ioreq->req.operation == BLKIF_OP_READ); + void *virt = ioreq->buf; - if (ioreq->v.niov == 0) { + if (ioreq->req.nr_segments == 0) { return 0; } - count = ioreq->v.niov; + count = ioreq->req.nr_segments; for (i = 0; i < count; i++) { - if (ioreq->req.operation == BLKIF_OP_READ) { - segs[i].flags = GNTCOPY_dest_gref; - segs[i].dest.foreign.ref = ioreq->refs[i]; - segs[i].dest.foreign.domid = ioreq->domids[i]; + if (to_domain) { + segs[i].dest.foreign.ref = ioreq->req.seg[i].gref; segs[i].dest.foreign.offset = ioreq->req.seg[i].first_sect * file_blk; - segs[i].source.virt = ioreq->v.iov[i].iov_base; + segs[i].source.virt = virt; } else { - segs[i].flags = GNTCOPY_source_gref; - segs[i].source.foreign.ref = ioreq->refs[i]; - segs[i].source.foreign.domid = ioreq->domids[i]; + segs[i].source.foreign.ref = ioreq->req.seg[i].gref; segs[i].source.foreign.offset = ioreq->req.seg[i].first_sect * file_blk; - segs[i].dest.virt = ioreq->v.iov[i].iov_base; + segs[i].dest.virt = virt; } segs[i].len = (ioreq->req.seg[i].last_sect - ioreq->req.seg[i].first_sect + 1) * file_blk; + virt += segs[i].len; } - rc = xengnttab_grant_copy(gnt, count, segs); + rc = xen_be_copy_grant_refs(xendev, to_domain, segs, count); if (rc) { - xen_pv_printf(&ioreq->blkdev->xendev, 0, + xen_pv_printf(xendev, 0, "failed to copy data %d\n", rc); ioreq->aio_errors++; return -1; } - for (i = 0; i < count; i++) { - if (segs[i].status != GNTST_okay) { - xen_pv_printf(&ioreq->blkdev->xendev, 3, - "failed to copy data %d for gref %d, domid %d\n", - segs[i].status, ioreq->refs[i], ioreq->domids[i]); - ioreq->aio_errors++; - rc = -1; - } - } - return rc; } -#else -static void ioreq_free_copy_buffers(struct ioreq *ioreq) -{ - abort(); -} - -static int ioreq_init_copy_buffers(struct ioreq *ioreq) -{ - abort(); -} - -static int ioreq_grant_copy(struct ioreq *ioreq) -{ - abort(); -} -#endif static int ioreq_runio_qemu_aio(struct ioreq *ioreq); @@ -602,11 +288,12 @@ static void qemu_aio_complete(void *opaque, int ret) { struct ioreq *ioreq = opaque; struct XenBlkDev *blkdev = ioreq->blkdev; + struct XenDevice *xendev = &blkdev->xendev; aio_context_acquire(blkdev->ctx); if (ret != 0) { - xen_pv_printf(&blkdev->xendev, 0, "%s I/O error\n", + xen_pv_printf(xendev, 0, "%s I/O error\n", ioreq->req.operation == BLKIF_OP_READ ? "read" : "write"); ioreq->aio_errors++; } @@ -621,32 +308,28 @@ static void qemu_aio_complete(void *opaque, int ret) goto done; } - if (xen_feature_grant_copy) { - switch (ioreq->req.operation) { - case BLKIF_OP_READ: - /* in case of failure ioreq->aio_errors is increased */ - if (ret == 0) { - ioreq_grant_copy(ioreq); - } - ioreq_free_copy_buffers(ioreq); - break; - case BLKIF_OP_WRITE: - case BLKIF_OP_FLUSH_DISKCACHE: - if (!ioreq->req.nr_segments) { - break; - } - ioreq_free_copy_buffers(ioreq); - break; - default: + switch (ioreq->req.operation) { + case BLKIF_OP_READ: + /* in case of failure ioreq->aio_errors is increased */ + if (ret == 0) { + ioreq_grant_copy(ioreq); + } + qemu_vfree(ioreq->buf); + break; + case BLKIF_OP_WRITE: + case BLKIF_OP_FLUSH_DISKCACHE: + if (!ioreq->req.nr_segments) { break; } + qemu_vfree(ioreq->buf); + break; + default: + break; } ioreq->status = ioreq->aio_errors ? BLKIF_RSP_ERROR : BLKIF_RSP_OKAY; - if (!xen_feature_grant_copy) { - ioreq_unmap(ioreq); - } ioreq_finish(ioreq); + switch (ioreq->req.operation) { case BLKIF_OP_WRITE: case BLKIF_OP_FLUSH_DISKCACHE: @@ -706,18 +389,13 @@ static int ioreq_runio_qemu_aio(struct ioreq *ioreq) { struct XenBlkDev *blkdev = ioreq->blkdev; - if (xen_feature_grant_copy) { - ioreq_init_copy_buffers(ioreq); - if (ioreq->req.nr_segments && (ioreq->req.operation == BLKIF_OP_WRITE || - ioreq->req.operation == BLKIF_OP_FLUSH_DISKCACHE) && - ioreq_grant_copy(ioreq)) { - ioreq_free_copy_buffers(ioreq); - goto err; - } - } else { - if (ioreq->req.nr_segments && ioreq_map(ioreq)) { - goto err; - } + ioreq->buf = qemu_memalign(XC_PAGE_SIZE, ioreq->size); + if (ioreq->req.nr_segments && + (ioreq->req.operation == BLKIF_OP_WRITE || + ioreq->req.operation == BLKIF_OP_FLUSH_DISKCACHE) && + ioreq_grant_copy(ioreq)) { + qemu_vfree(ioreq->buf); + goto err; } ioreq->aio_inflight++; @@ -728,6 +406,7 @@ static int ioreq_runio_qemu_aio(struct ioreq *ioreq) switch (ioreq->req.operation) { case BLKIF_OP_READ: + qemu_iovec_add(&ioreq->v, ioreq->buf, ioreq->size); block_acct_start(blk_get_stats(blkdev->blk), &ioreq->acct, ioreq->v.size, BLOCK_ACCT_READ); ioreq->aio_inflight++; @@ -740,6 +419,7 @@ static int ioreq_runio_qemu_aio(struct ioreq *ioreq) break; } + qemu_iovec_add(&ioreq->v, ioreq->buf, ioreq->size); block_acct_start(blk_get_stats(blkdev->blk), &ioreq->acct, ioreq->v.size, ioreq->req.operation == BLKIF_OP_WRITE ? @@ -758,9 +438,6 @@ static int ioreq_runio_qemu_aio(struct ioreq *ioreq) } default: /* unknown operation (shouldn't happen -- parse catches this) */ - if (!xen_feature_grant_copy) { - ioreq_unmap(ioreq); - } goto err; } @@ -946,24 +623,21 @@ static void blk_alloc(struct XenDevice *xendev) blkdev->ctx = iothread_get_aio_context(blkdev->iothread); blkdev->bh = aio_bh_new(blkdev->ctx, blk_bh, blkdev); - - if (xen_mode != XEN_EMULATE) { - batch_maps = 1; - } } static void blk_parse_discard(struct XenBlkDev *blkdev) { + struct XenDevice *xendev = &blkdev->xendev; int enable; blkdev->feature_discard = true; - if (xenstore_read_be_int(&blkdev->xendev, "discard-enable", &enable) == 0) { + if (xenstore_read_be_int(xendev, "discard-enable", &enable) == 0) { blkdev->feature_discard = !!enable; } if (blkdev->feature_discard) { - xenstore_write_be_int(&blkdev->xendev, "feature-discard", 1); + xenstore_write_be_int(xendev, "feature-discard", 1); } } @@ -978,7 +652,7 @@ static int blk_init(struct XenDevice *xendev) /* read xenstore entries */ if (blkdev->params == NULL) { char *h = NULL; - blkdev->params = xenstore_read_be_str(&blkdev->xendev, "params"); + blkdev->params = xenstore_read_be_str(xendev, "params"); if (blkdev->params != NULL) { h = strchr(blkdev->params, ':'); } @@ -998,18 +672,18 @@ static int blk_init(struct XenDevice *xendev) blkdev->fileproto = "vpc"; } if (blkdev->mode == NULL) { - blkdev->mode = xenstore_read_be_str(&blkdev->xendev, "mode"); + blkdev->mode = xenstore_read_be_str(xendev, "mode"); } if (blkdev->type == NULL) { - blkdev->type = xenstore_read_be_str(&blkdev->xendev, "type"); + blkdev->type = xenstore_read_be_str(xendev, "type"); } if (blkdev->dev == NULL) { - blkdev->dev = xenstore_read_be_str(&blkdev->xendev, "dev"); + blkdev->dev = xenstore_read_be_str(xendev, "dev"); } if (blkdev->devtype == NULL) { - blkdev->devtype = xenstore_read_be_str(&blkdev->xendev, "device-type"); + blkdev->devtype = xenstore_read_be_str(xendev, "device-type"); } - directiosafe = xenstore_read_be_str(&blkdev->xendev, "direct-io-safe"); + directiosafe = xenstore_read_be_str(xendev, "direct-io-safe"); blkdev->directiosafe = (directiosafe && atoi(directiosafe)); /* do we have all we need? */ @@ -1032,18 +706,13 @@ static int blk_init(struct XenDevice *xendev) blkdev->file_blk = BLOCK_SIZE; - xen_pv_printf(&blkdev->xendev, 3, "grant copy operation %s\n", - xen_feature_grant_copy ? "enabled" : "disabled"); - /* fill info * blk_connect supplies sector-size and sectors */ - xenstore_write_be_int(&blkdev->xendev, "feature-flush-cache", 1); - xenstore_write_be_int(&blkdev->xendev, "feature-persistent", - !xen_feature_grant_copy); - xenstore_write_be_int(&blkdev->xendev, "info", info); + xenstore_write_be_int(xendev, "feature-flush-cache", 1); + xenstore_write_be_int(xendev, "info", info); - xenstore_write_be_int(&blkdev->xendev, "max-ring-page-order", + xenstore_write_be_int(xendev, "max-ring-page-order", MAX_RING_PAGE_ORDER); blk_parse_discard(blkdev); @@ -1067,25 +736,15 @@ out_error: return -1; } -/* - * We need to account for the grant allocations requiring contiguous - * chunks; the worst case number would be - * max_req * max_seg + (max_req - 1) * (max_seg - 1) + 1, - * but in order to keep things simple just use - * 2 * max_req * max_seg. - */ -#define MAX_GRANTS(max_req, max_seg) (2 * (max_req) * (max_seg)) - static int blk_connect(struct XenDevice *xendev) { struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); - int pers, index, qflags; + int index, qflags; bool readonly = true; bool writethrough = true; int order, ring_ref; unsigned int ring_size, max_grants; unsigned int i; - uint32_t *domids; trace_xen_disk_connect(xendev->name); @@ -1105,7 +764,7 @@ static int blk_connect(struct XenDevice *xendev) } /* init qemu block driver */ - index = (blkdev->xendev.dev - 202 * 256) / 16; + index = (xendev->dev - 202 * 256) / 16; blkdev->dinfo = drive_get(IF_XEN, 0, index); if (!blkdev->dinfo) { Error *local_err = NULL; @@ -1117,11 +776,11 @@ static int blk_connect(struct XenDevice *xendev) } /* setup via xenbus -> create new block driver instance */ - xen_pv_printf(&blkdev->xendev, 2, "create new bdrv (xenbus setup)\n"); + xen_pv_printf(xendev, 2, "create new bdrv (xenbus setup)\n"); blkdev->blk = blk_new_open(blkdev->filename, NULL, options, qflags, &local_err); if (!blkdev->blk) { - xen_pv_printf(&blkdev->xendev, 0, "error: %s\n", + xen_pv_printf(xendev, 0, "error: %s\n", error_get_pretty(local_err)); error_free(local_err); return -1; @@ -1129,11 +788,11 @@ static int blk_connect(struct XenDevice *xendev) blk_set_enable_write_cache(blkdev->blk, !writethrough); } else { /* setup via qemu cmdline -> already setup for us */ - xen_pv_printf(&blkdev->xendev, 2, + xen_pv_printf(xendev, 2, "get configured bdrv (cmdline setup)\n"); blkdev->blk = blk_by_legacy_dinfo(blkdev->dinfo); if (blk_is_read_only(blkdev->blk) && !readonly) { - xen_pv_printf(&blkdev->xendev, 0, "Unexpected read-only drive"); + xen_pv_printf(xendev, 0, "Unexpected read-only drive"); blkdev->blk = NULL; return -1; } @@ -1146,7 +805,7 @@ static int blk_connect(struct XenDevice *xendev) if (blkdev->file_size < 0) { BlockDriverState *bs = blk_bs(blkdev->blk); const char *drv_name = bs ? bdrv_get_format_name(bs) : NULL; - xen_pv_printf(&blkdev->xendev, 1, "blk_getlength: %d (%s) | drv %s\n", + xen_pv_printf(xendev, 1, "blk_getlength: %d (%s) | drv %s\n", (int)blkdev->file_size, strerror(-blkdev->file_size), drv_name ?: "-"); blkdev->file_size = 0; @@ -1158,15 +817,15 @@ static int blk_connect(struct XenDevice *xendev) blkdev->file_size, blkdev->file_size >> 20); /* Fill in number of sector size and number of sectors */ - xenstore_write_be_int(&blkdev->xendev, "sector-size", blkdev->file_blk); - xenstore_write_be_int64(&blkdev->xendev, "sectors", + xenstore_write_be_int(xendev, "sector-size", blkdev->file_blk); + xenstore_write_be_int64(xendev, "sectors", blkdev->file_size / blkdev->file_blk); - if (xenstore_read_fe_int(&blkdev->xendev, "ring-page-order", + if (xenstore_read_fe_int(xendev, "ring-page-order", &order) == -1) { blkdev->nr_ring_ref = 1; - if (xenstore_read_fe_int(&blkdev->xendev, "ring-ref", + if (xenstore_read_fe_int(xendev, "ring-ref", &ring_ref) == -1) { return -1; } @@ -1183,7 +842,7 @@ static int blk_connect(struct XenDevice *xendev) return -1; } - if (xenstore_read_fe_int(&blkdev->xendev, key, + if (xenstore_read_fe_int(xendev, key, &ring_ref) == -1) { g_free(key); return -1; @@ -1198,23 +857,18 @@ static int blk_connect(struct XenDevice *xendev) return -1; } - if (xenstore_read_fe_int(&blkdev->xendev, "event-channel", - &blkdev->xendev.remote_port) == -1) { + if (xenstore_read_fe_int(xendev, "event-channel", + &xendev->remote_port) == -1) { return -1; } - if (xenstore_read_fe_int(&blkdev->xendev, "feature-persistent", &pers)) { - blkdev->feature_persistent = FALSE; - } else { - blkdev->feature_persistent = !!pers; - } - if (!blkdev->xendev.protocol) { + if (!xendev->protocol) { blkdev->protocol = BLKIF_PROTOCOL_NATIVE; - } else if (strcmp(blkdev->xendev.protocol, XEN_IO_PROTO_ABI_NATIVE) == 0) { + } else if (strcmp(xendev->protocol, XEN_IO_PROTO_ABI_NATIVE) == 0) { blkdev->protocol = BLKIF_PROTOCOL_NATIVE; - } else if (strcmp(blkdev->xendev.protocol, XEN_IO_PROTO_ABI_X86_32) == 0) { + } else if (strcmp(xendev->protocol, XEN_IO_PROTO_ABI_X86_32) == 0) { blkdev->protocol = BLKIF_PROTOCOL_X86_32; - } else if (strcmp(blkdev->xendev.protocol, XEN_IO_PROTO_ABI_X86_64) == 0) { + } else if (strcmp(xendev->protocol, XEN_IO_PROTO_ABI_X86_64) == 0) { blkdev->protocol = BLKIF_PROTOCOL_X86_64; } else { blkdev->protocol = BLKIF_PROTOCOL_NATIVE; @@ -1241,43 +895,17 @@ static int blk_connect(struct XenDevice *xendev) return -1; } - /* Calculate the maximum number of grants needed by ioreqs */ - max_grants = MAX_GRANTS(blkdev->max_requests, - BLKIF_MAX_SEGMENTS_PER_REQUEST); /* Add on the number needed for the ring pages */ - max_grants += blkdev->nr_ring_ref; - - blkdev->xendev.gnttabdev = xengnttab_open(NULL, 0); - if (blkdev->xendev.gnttabdev == NULL) { - xen_pv_printf(xendev, 0, "xengnttab_open failed: %s\n", - strerror(errno)); - return -1; - } - if (xengnttab_set_max_grants(blkdev->xendev.gnttabdev, max_grants)) { - xen_pv_printf(xendev, 0, "xengnttab_set_max_grants failed: %s\n", - strerror(errno)); - return -1; - } - - domids = g_new0(uint32_t, blkdev->nr_ring_ref); - for (i = 0; i < blkdev->nr_ring_ref; i++) { - domids[i] = blkdev->xendev.dom; - } - - blkdev->sring = xengnttab_map_grant_refs(blkdev->xendev.gnttabdev, - blkdev->nr_ring_ref, - domids, - blkdev->ring_ref, - PROT_READ | PROT_WRITE); - - g_free(domids); + max_grants = blkdev->nr_ring_ref; + xen_be_set_max_grant_refs(xendev, max_grants); + blkdev->sring = xen_be_map_grant_refs(xendev, blkdev->ring_ref, + blkdev->nr_ring_ref, + PROT_READ | PROT_WRITE); if (!blkdev->sring) { return -1; } - blkdev->cnt_map++; - switch (blkdev->protocol) { case BLKIF_PROTOCOL_NATIVE: { @@ -1301,27 +929,14 @@ static int blk_connect(struct XenDevice *xendev) } } - if (blkdev->feature_persistent) { - /* Init persistent grants */ - blkdev->max_grants = blkdev->max_requests * - BLKIF_MAX_SEGMENTS_PER_REQUEST; - blkdev->persistent_gnts = g_tree_new_full((GCompareDataFunc)int_cmp, - NULL, NULL, - batch_maps ? - (GDestroyNotify)g_free : - (GDestroyNotify)destroy_grant); - blkdev->persistent_regions = NULL; - blkdev->persistent_gnt_count = 0; - } - blk_set_aio_context(blkdev->blk, blkdev->ctx); - xen_be_bind_evtchn(&blkdev->xendev); + xen_be_bind_evtchn(xendev); - xen_pv_printf(&blkdev->xendev, 1, "ok: proto %s, nr-ring-ref %u, " + xen_pv_printf(xendev, 1, "ok: proto %s, nr-ring-ref %u, " "remote port %d, local port %d\n", - blkdev->xendev.protocol, blkdev->nr_ring_ref, - blkdev->xendev.remote_port, blkdev->xendev.local_port); + xendev->protocol, blkdev->nr_ring_ref, + xendev->remote_port, xendev->local_port); return 0; } @@ -1339,41 +954,15 @@ static void blk_disconnect(struct XenDevice *xendev) blk_unref(blkdev->blk); blkdev->blk = NULL; } - xen_pv_unbind_evtchn(&blkdev->xendev); + xen_pv_unbind_evtchn(xendev); aio_context_release(blkdev->ctx); if (blkdev->sring) { - xengnttab_unmap(blkdev->xendev.gnttabdev, blkdev->sring, - blkdev->nr_ring_ref); - blkdev->cnt_map--; + xen_be_unmap_grant_refs(xendev, blkdev->sring, + blkdev->nr_ring_ref); blkdev->sring = NULL; } - - /* - * Unmap persistent grants before switching to the closed state - * so the frontend can free them. - * - * In the !batch_maps case g_tree_destroy will take care of unmapping - * the grant, but in the batch_maps case we need to iterate over every - * region in persistent_regions and unmap it. - */ - if (blkdev->feature_persistent) { - g_tree_destroy(blkdev->persistent_gnts); - assert(batch_maps || blkdev->persistent_gnt_count == 0); - if (batch_maps) { - blkdev->persistent_gnt_count = 0; - g_slist_foreach(blkdev->persistent_regions, - (GFunc)remove_persistent_region, blkdev); - g_slist_free(blkdev->persistent_regions); - } - blkdev->feature_persistent = false; - } - - if (blkdev->xendev.gnttabdev) { - xengnttab_close(blkdev->xendev.gnttabdev); - blkdev->xendev.gnttabdev = NULL; - } } static int blk_free(struct XenDevice *xendev) @@ -1410,10 +999,11 @@ static void blk_event(struct XenDevice *xendev) } struct XenDevOps xen_blkdev_ops = { + .flags = DEVOPS_FLAG_NEED_GNTDEV, .size = sizeof(struct XenBlkDev), .alloc = blk_alloc, .init = blk_init, - .initialise = blk_connect, + .initialise = blk_connect, .disconnect = blk_disconnect, .event = blk_event, .free = blk_free, diff --git a/hw/char/xen_console.c b/hw/char/xen_console.c index bdfaa40ed3..8b4b4bf523 100644 --- a/hw/char/xen_console.c +++ b/hw/char/xen_console.c @@ -233,12 +233,11 @@ static int con_initialise(struct XenDevice *xendev) if (!xendev->dev) { xen_pfn_t mfn = con->ring_ref; con->sring = xenforeignmemory_map(xen_fmem, con->xendev.dom, - PROT_READ|PROT_WRITE, + PROT_READ | PROT_WRITE, 1, &mfn, NULL); } else { - con->sring = xengnttab_map_grant_ref(xendev->gnttabdev, con->xendev.dom, - con->ring_ref, - PROT_READ|PROT_WRITE); + con->sring = xen_be_map_grant_ref(xendev, con->ring_ref, + PROT_READ | PROT_WRITE); } if (!con->sring) return -1; @@ -267,7 +266,7 @@ static void con_disconnect(struct XenDevice *xendev) if (!xendev->dev) { xenforeignmemory_unmap(xen_fmem, con->sring, 1); } else { - xengnttab_unmap(xendev->gnttabdev, con->sring, 1); + xen_be_unmap_grant_ref(xendev, con->sring); } con->sring = NULL; } diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index fb31de9416..b5a09b7908 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -128,6 +128,22 @@ static uint64_t vtd_set_clear_mask_quad(IntelIOMMUState *s, hwaddr addr, return new_val; } +static inline void vtd_iommu_lock(IntelIOMMUState *s) +{ + qemu_mutex_lock(&s->iommu_lock); +} + +static inline void vtd_iommu_unlock(IntelIOMMUState *s) +{ + qemu_mutex_unlock(&s->iommu_lock); +} + +/* Whether the address space needs to notify new mappings */ +static inline gboolean vtd_as_has_map_notifier(VTDAddressSpace *as) +{ + return as->notifier_flags & IOMMU_NOTIFIER_MAP; +} + /* GHashTable functions */ static gboolean vtd_uint64_equal(gconstpointer v1, gconstpointer v2) { @@ -172,9 +188,9 @@ static gboolean vtd_hash_remove_by_page(gpointer key, gpointer value, } /* Reset all the gen of VTDAddressSpace to zero and set the gen of - * IntelIOMMUState to 1. + * IntelIOMMUState to 1. Must be called with IOMMU lock held. */ -static void vtd_reset_context_cache(IntelIOMMUState *s) +static void vtd_reset_context_cache_locked(IntelIOMMUState *s) { VTDAddressSpace *vtd_as; VTDBus *vtd_bus; @@ -197,12 +213,20 @@ static void vtd_reset_context_cache(IntelIOMMUState *s) s->context_cache_gen = 1; } -static void vtd_reset_iotlb(IntelIOMMUState *s) +/* Must be called with IOMMU lock held. */ +static void vtd_reset_iotlb_locked(IntelIOMMUState *s) { assert(s->iotlb); g_hash_table_remove_all(s->iotlb); } +static void vtd_reset_iotlb(IntelIOMMUState *s) +{ + vtd_iommu_lock(s); + vtd_reset_iotlb_locked(s); + vtd_iommu_unlock(s); +} + static uint64_t vtd_get_iotlb_key(uint64_t gfn, uint16_t source_id, uint32_t level) { @@ -215,6 +239,7 @@ static uint64_t vtd_get_iotlb_gfn(hwaddr addr, uint32_t level) return (addr & vtd_slpt_level_page_mask(level)) >> VTD_PAGE_SHIFT_4K; } +/* Must be called with IOMMU lock held */ static VTDIOTLBEntry *vtd_lookup_iotlb(IntelIOMMUState *s, uint16_t source_id, hwaddr addr) { @@ -235,6 +260,7 @@ out: return entry; } +/* Must be with IOMMU lock held */ static void vtd_update_iotlb(IntelIOMMUState *s, uint16_t source_id, uint16_t domain_id, hwaddr addr, uint64_t slpte, uint8_t access_flags, uint32_t level) @@ -246,7 +272,7 @@ static void vtd_update_iotlb(IntelIOMMUState *s, uint16_t source_id, trace_vtd_iotlb_page_update(source_id, addr, slpte, domain_id); if (g_hash_table_size(s->iotlb) >= VTD_IOTLB_MAX_SIZE) { trace_vtd_iotlb_reset("iotlb exceeds size limit"); - vtd_reset_iotlb(s); + vtd_reset_iotlb_locked(s); } entry->gfn = gfn; @@ -723,22 +749,116 @@ static int vtd_iova_to_slpte(VTDContextEntry *ce, uint64_t iova, bool is_write, typedef int (*vtd_page_walk_hook)(IOMMUTLBEntry *entry, void *private); /** + * Constant information used during page walking + * + * @hook_fn: hook func to be called when detected page + * @private: private data to be passed into hook func + * @notify_unmap: whether we should notify invalid entries + * @as: VT-d address space of the device + * @aw: maximum address width + * @domain: domain ID of the page walk + */ +typedef struct { + VTDAddressSpace *as; + vtd_page_walk_hook hook_fn; + void *private; + bool notify_unmap; + uint8_t aw; + uint16_t domain_id; +} vtd_page_walk_info; + +static int vtd_page_walk_one(IOMMUTLBEntry *entry, vtd_page_walk_info *info) +{ + VTDAddressSpace *as = info->as; + vtd_page_walk_hook hook_fn = info->hook_fn; + void *private = info->private; + DMAMap target = { + .iova = entry->iova, + .size = entry->addr_mask, + .translated_addr = entry->translated_addr, + .perm = entry->perm, + }; + DMAMap *mapped = iova_tree_find(as->iova_tree, &target); + + if (entry->perm == IOMMU_NONE && !info->notify_unmap) { + trace_vtd_page_walk_one_skip_unmap(entry->iova, entry->addr_mask); + return 0; + } + + assert(hook_fn); + + /* Update local IOVA mapped ranges */ + if (entry->perm) { + if (mapped) { + /* If it's exactly the same translation, skip */ + if (!memcmp(mapped, &target, sizeof(target))) { + trace_vtd_page_walk_one_skip_map(entry->iova, entry->addr_mask, + entry->translated_addr); + return 0; + } else { + /* + * Translation changed. Normally this should not + * happen, but it can happen when with buggy guest + * OSes. Note that there will be a small window that + * we don't have map at all. But that's the best + * effort we can do. The ideal way to emulate this is + * atomically modify the PTE to follow what has + * changed, but we can't. One example is that vfio + * driver only has VFIO_IOMMU_[UN]MAP_DMA but no + * interface to modify a mapping (meanwhile it seems + * meaningless to even provide one). Anyway, let's + * mark this as a TODO in case one day we'll have + * a better solution. + */ + IOMMUAccessFlags cache_perm = entry->perm; + int ret; + + /* Emulate an UNMAP */ + entry->perm = IOMMU_NONE; + trace_vtd_page_walk_one(info->domain_id, + entry->iova, + entry->translated_addr, + entry->addr_mask, + entry->perm); + ret = hook_fn(entry, private); + if (ret) { + return ret; + } + /* Drop any existing mapping */ + iova_tree_remove(as->iova_tree, &target); + /* Recover the correct permission */ + entry->perm = cache_perm; + } + } + iova_tree_insert(as->iova_tree, &target); + } else { + if (!mapped) { + /* Skip since we didn't map this range at all */ + trace_vtd_page_walk_one_skip_unmap(entry->iova, entry->addr_mask); + return 0; + } + iova_tree_remove(as->iova_tree, &target); + } + + trace_vtd_page_walk_one(info->domain_id, entry->iova, + entry->translated_addr, entry->addr_mask, + entry->perm); + return hook_fn(entry, private); +} + +/** * vtd_page_walk_level - walk over specific level for IOVA range * * @addr: base GPA addr to start the walk * @start: IOVA range start address * @end: IOVA range end address (start <= addr < end) - * @hook_fn: hook func to be called when detected page - * @private: private data to be passed into hook func * @read: whether parent level has read permission * @write: whether parent level has write permission - * @notify_unmap: whether we should notify invalid entries - * @aw: maximum address width + * @info: constant information for the page walk */ static int vtd_page_walk_level(dma_addr_t addr, uint64_t start, - uint64_t end, vtd_page_walk_hook hook_fn, - void *private, uint32_t level, bool read, - bool write, bool notify_unmap, uint8_t aw) + uint64_t end, uint32_t level, bool read, + bool write, vtd_page_walk_info *info) { bool read_cur, write_cur, entry_valid; uint32_t offset; @@ -781,37 +901,34 @@ static int vtd_page_walk_level(dma_addr_t addr, uint64_t start, */ entry_valid = read_cur | write_cur; - if (vtd_is_last_slpte(slpte, level)) { + if (!vtd_is_last_slpte(slpte, level) && entry_valid) { + /* + * This is a valid PDE (or even bigger than PDE). We need + * to walk one further level. + */ + ret = vtd_page_walk_level(vtd_get_slpte_addr(slpte, info->aw), + iova, MIN(iova_next, end), level - 1, + read_cur, write_cur, info); + } else { + /* + * This means we are either: + * + * (1) the real page entry (either 4K page, or huge page) + * (2) the whole range is invalid + * + * In either case, we send an IOTLB notification down. + */ entry.target_as = &address_space_memory; entry.iova = iova & subpage_mask; - /* NOTE: this is only meaningful if entry_valid == true */ - entry.translated_addr = vtd_get_slpte_addr(slpte, aw); - entry.addr_mask = ~subpage_mask; entry.perm = IOMMU_ACCESS_FLAG(read_cur, write_cur); - if (!entry_valid && !notify_unmap) { - trace_vtd_page_walk_skip_perm(iova, iova_next); - goto next; - } - trace_vtd_page_walk_one(level, entry.iova, entry.translated_addr, - entry.addr_mask, entry.perm); - if (hook_fn) { - ret = hook_fn(&entry, private); - if (ret < 0) { - return ret; - } - } - } else { - if (!entry_valid) { - trace_vtd_page_walk_skip_perm(iova, iova_next); - goto next; - } - ret = vtd_page_walk_level(vtd_get_slpte_addr(slpte, aw), iova, - MIN(iova_next, end), hook_fn, private, - level - 1, read_cur, write_cur, - notify_unmap, aw); - if (ret < 0) { - return ret; - } + entry.addr_mask = ~subpage_mask; + /* NOTE: this is only meaningful if entry_valid == true */ + entry.translated_addr = vtd_get_slpte_addr(slpte, info->aw); + ret = vtd_page_walk_one(&entry, info); + } + + if (ret < 0) { + return ret; } next: @@ -827,28 +944,24 @@ next: * @ce: context entry to walk upon * @start: IOVA address to start the walk * @end: IOVA range end address (start <= addr < end) - * @hook_fn: the hook that to be called for each detected area - * @private: private data for the hook function - * @aw: maximum address width + * @info: page walking information struct */ static int vtd_page_walk(VTDContextEntry *ce, uint64_t start, uint64_t end, - vtd_page_walk_hook hook_fn, void *private, - bool notify_unmap, uint8_t aw) + vtd_page_walk_info *info) { dma_addr_t addr = vtd_ce_get_slpt_base(ce); uint32_t level = vtd_ce_get_level(ce); - if (!vtd_iova_range_check(start, ce, aw)) { + if (!vtd_iova_range_check(start, ce, info->aw)) { return -VTD_FR_ADDR_BEYOND_MGAW; } - if (!vtd_iova_range_check(end, ce, aw)) { + if (!vtd_iova_range_check(end, ce, info->aw)) { /* Fix end so that it reaches the maximum */ - end = vtd_iova_limit(ce, aw); + end = vtd_iova_limit(ce, info->aw); } - return vtd_page_walk_level(addr, start, end, hook_fn, private, - level, true, true, notify_unmap, aw); + return vtd_page_walk_level(addr, start, end, level, true, true, info); } /* Map a device to its corresponding domain (context-entry) */ @@ -907,6 +1020,58 @@ static int vtd_dev_to_context_entry(IntelIOMMUState *s, uint8_t bus_num, return 0; } +static int vtd_sync_shadow_page_hook(IOMMUTLBEntry *entry, + void *private) +{ + memory_region_notify_iommu((IOMMUMemoryRegion *)private, *entry); + return 0; +} + +/* If context entry is NULL, we'll try to fetch it on our own. */ +static int vtd_sync_shadow_page_table_range(VTDAddressSpace *vtd_as, + VTDContextEntry *ce, + hwaddr addr, hwaddr size) +{ + IntelIOMMUState *s = vtd_as->iommu_state; + vtd_page_walk_info info = { + .hook_fn = vtd_sync_shadow_page_hook, + .private = (void *)&vtd_as->iommu, + .notify_unmap = true, + .aw = s->aw_bits, + .as = vtd_as, + }; + VTDContextEntry ce_cache; + int ret; + + if (ce) { + /* If the caller provided context entry, use it */ + ce_cache = *ce; + } else { + /* If the caller didn't provide ce, try to fetch */ + ret = vtd_dev_to_context_entry(s, pci_bus_num(vtd_as->bus), + vtd_as->devfn, &ce_cache); + if (ret) { + /* + * This should not really happen, but in case it happens, + * we just skip the sync for this time. After all we even + * don't have the root table pointer! + */ + trace_vtd_err("Detected invalid context entry when " + "trying to sync shadow page table"); + return 0; + } + } + + info.domain_id = VTD_CONTEXT_ENTRY_DID(ce_cache.hi); + + return vtd_page_walk(&ce_cache, addr, addr + size, &info); +} + +static int vtd_sync_shadow_page_table(VTDAddressSpace *vtd_as) +{ + return vtd_sync_shadow_page_table_range(vtd_as, NULL, 0, UINT64_MAX); +} + /* * Fetch translation type for specific device. Returns <0 if error * happens, otherwise return the shifted type to check against @@ -1088,7 +1253,7 @@ static bool vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus, IntelIOMMUState *s = vtd_as->iommu_state; VTDContextEntry ce; uint8_t bus_num = pci_bus_num(bus); - VTDContextCacheEntry *cc_entry = &vtd_as->context_cache_entry; + VTDContextCacheEntry *cc_entry; uint64_t slpte, page_mask; uint32_t level; uint16_t source_id = vtd_make_source_id(bus_num, devfn); @@ -1105,6 +1270,10 @@ static bool vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus, */ assert(!vtd_is_interrupt_addr(addr)); + vtd_iommu_lock(s); + + cc_entry = &vtd_as->context_cache_entry; + /* Try to fetch slpte form IOTLB */ iotlb_entry = vtd_lookup_iotlb(s, source_id, addr); if (iotlb_entry) { @@ -1164,7 +1333,7 @@ static bool vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus, * IOMMU region can be swapped back. */ vtd_pt_enable_fast_path(s, source_id); - + vtd_iommu_unlock(s); return true; } @@ -1185,6 +1354,7 @@ static bool vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus, vtd_update_iotlb(s, source_id, VTD_CONTEXT_ENTRY_DID(ce.hi), addr, slpte, access_flags, level); out: + vtd_iommu_unlock(s); entry->iova = addr & page_mask; entry->translated_addr = vtd_get_slpte_addr(slpte, s->aw_bits) & page_mask; entry->addr_mask = ~page_mask; @@ -1192,6 +1362,7 @@ out: return true; error: + vtd_iommu_unlock(s); entry->iova = 0; entry->translated_addr = 0; entry->addr_mask = 0; @@ -1230,20 +1401,23 @@ static void vtd_interrupt_remap_table_setup(IntelIOMMUState *s) static void vtd_iommu_replay_all(IntelIOMMUState *s) { - IntelIOMMUNotifierNode *node; + VTDAddressSpace *vtd_as; - QLIST_FOREACH(node, &s->notifiers_list, next) { - memory_region_iommu_replay_all(&node->vtd_as->iommu); + QLIST_FOREACH(vtd_as, &s->vtd_as_with_notifiers, next) { + vtd_sync_shadow_page_table(vtd_as); } } static void vtd_context_global_invalidate(IntelIOMMUState *s) { trace_vtd_inv_desc_cc_global(); + /* Protects context cache */ + vtd_iommu_lock(s); s->context_cache_gen++; if (s->context_cache_gen == VTD_CONTEXT_CACHE_GEN_MAX) { - vtd_reset_context_cache(s); + vtd_reset_context_cache_locked(s); } + vtd_iommu_unlock(s); vtd_switch_address_space_all(s); /* * From VT-d spec 6.5.2.1, a global context entry invalidation @@ -1295,7 +1469,9 @@ static void vtd_context_device_invalidate(IntelIOMMUState *s, if (vtd_as && ((devfn_it & mask) == (devfn & mask))) { trace_vtd_inv_desc_cc_device(bus_n, VTD_PCI_SLOT(devfn_it), VTD_PCI_FUNC(devfn_it)); + vtd_iommu_lock(s); vtd_as->context_cache_entry.context_cache_gen = 0; + vtd_iommu_unlock(s); /* * Do switch address space when needed, in case if the * device passthrough bit is switched. @@ -1303,14 +1479,13 @@ static void vtd_context_device_invalidate(IntelIOMMUState *s, vtd_switch_address_space(vtd_as); /* * So a device is moving out of (or moving into) a - * domain, a replay() suites here to notify all the - * IOMMU_NOTIFIER_MAP registers about this change. + * domain, resync the shadow page table. * This won't bring bad even if we have no such * notifier registered - the IOMMU notification * framework will skip MAP notifications if that * happened. */ - memory_region_iommu_replay_all(&vtd_as->iommu); + vtd_sync_shadow_page_table(vtd_as); } } } @@ -1354,48 +1529,60 @@ static void vtd_iotlb_global_invalidate(IntelIOMMUState *s) static void vtd_iotlb_domain_invalidate(IntelIOMMUState *s, uint16_t domain_id) { - IntelIOMMUNotifierNode *node; VTDContextEntry ce; VTDAddressSpace *vtd_as; trace_vtd_inv_desc_iotlb_domain(domain_id); + vtd_iommu_lock(s); g_hash_table_foreach_remove(s->iotlb, vtd_hash_remove_by_domain, &domain_id); + vtd_iommu_unlock(s); - QLIST_FOREACH(node, &s->notifiers_list, next) { - vtd_as = node->vtd_as; + QLIST_FOREACH(vtd_as, &s->vtd_as_with_notifiers, next) { if (!vtd_dev_to_context_entry(s, pci_bus_num(vtd_as->bus), vtd_as->devfn, &ce) && domain_id == VTD_CONTEXT_ENTRY_DID(ce.hi)) { - memory_region_iommu_replay_all(&vtd_as->iommu); + vtd_sync_shadow_page_table(vtd_as); } } } -static int vtd_page_invalidate_notify_hook(IOMMUTLBEntry *entry, - void *private) -{ - memory_region_notify_iommu((IOMMUMemoryRegion *)private, *entry); - return 0; -} - static void vtd_iotlb_page_invalidate_notify(IntelIOMMUState *s, uint16_t domain_id, hwaddr addr, uint8_t am) { - IntelIOMMUNotifierNode *node; + VTDAddressSpace *vtd_as; VTDContextEntry ce; int ret; + hwaddr size = (1 << am) * VTD_PAGE_SIZE; - QLIST_FOREACH(node, &(s->notifiers_list), next) { - VTDAddressSpace *vtd_as = node->vtd_as; + QLIST_FOREACH(vtd_as, &(s->vtd_as_with_notifiers), next) { ret = vtd_dev_to_context_entry(s, pci_bus_num(vtd_as->bus), vtd_as->devfn, &ce); if (!ret && domain_id == VTD_CONTEXT_ENTRY_DID(ce.hi)) { - vtd_page_walk(&ce, addr, addr + (1 << am) * VTD_PAGE_SIZE, - vtd_page_invalidate_notify_hook, - (void *)&vtd_as->iommu, true, s->aw_bits); + if (vtd_as_has_map_notifier(vtd_as)) { + /* + * As long as we have MAP notifications registered in + * any of our IOMMU notifiers, we need to sync the + * shadow page table. + */ + vtd_sync_shadow_page_table_range(vtd_as, &ce, addr, size); + } else { + /* + * For UNMAP-only notifiers, we don't need to walk the + * page tables. We just deliver the PSI down to + * invalidate caches. + */ + IOMMUTLBEntry entry = { + .target_as = &address_space_memory, + .iova = addr, + .translated_addr = 0, + .addr_mask = size - 1, + .perm = IOMMU_NONE, + }; + memory_region_notify_iommu(&vtd_as->iommu, entry); + } } } } @@ -1411,7 +1598,9 @@ static void vtd_iotlb_page_invalidate(IntelIOMMUState *s, uint16_t domain_id, info.domain_id = domain_id; info.addr = addr; info.mask = ~((1 << am) - 1); + vtd_iommu_lock(s); g_hash_table_foreach_remove(s->iotlb, vtd_hash_remove_by_page, &info); + vtd_iommu_unlock(s); vtd_iotlb_page_invalidate_notify(s, domain_id, addr, am); } @@ -2326,8 +2515,6 @@ static void vtd_iommu_notify_flag_changed(IOMMUMemoryRegion *iommu, { VTDAddressSpace *vtd_as = container_of(iommu, VTDAddressSpace, iommu); IntelIOMMUState *s = vtd_as->iommu_state; - IntelIOMMUNotifierNode *node = NULL; - IntelIOMMUNotifierNode *next_node = NULL; if (!s->caching_mode && new & IOMMU_NOTIFIER_MAP) { error_report("We need to set caching-mode=1 for intel-iommu to enable " @@ -2335,22 +2522,13 @@ static void vtd_iommu_notify_flag_changed(IOMMUMemoryRegion *iommu, exit(1); } - if (old == IOMMU_NOTIFIER_NONE) { - node = g_malloc0(sizeof(*node)); - node->vtd_as = vtd_as; - QLIST_INSERT_HEAD(&s->notifiers_list, node, next); - return; - } + /* Update per-address-space notifier flags */ + vtd_as->notifier_flags = new; - /* update notifier node with new flags */ - QLIST_FOREACH_SAFE(node, &s->notifiers_list, next, next_node) { - if (node->vtd_as == vtd_as) { - if (new == IOMMU_NOTIFIER_NONE) { - QLIST_REMOVE(node, next); - g_free(node); - } - return; - } + if (old == IOMMU_NOTIFIER_NONE) { + QLIST_INSERT_HEAD(&s->vtd_as_with_notifiers, vtd_as, next); + } else if (new == IOMMU_NOTIFIER_NONE) { + QLIST_REMOVE(vtd_as, next); } } @@ -2719,6 +2897,7 @@ VTDAddressSpace *vtd_find_add_as(IntelIOMMUState *s, PCIBus *bus, int devfn) vtd_dev_as->devfn = (uint8_t)devfn; vtd_dev_as->iommu_state = s; vtd_dev_as->context_cache_entry.context_cache_gen = 0; + vtd_dev_as->iova_tree = iova_tree_new(); /* * Memory region relationships looks like (Address range shows @@ -2771,6 +2950,7 @@ static void vtd_address_space_unmap(VTDAddressSpace *as, IOMMUNotifier *n) hwaddr start = n->start; hwaddr end = n->end; IntelIOMMUState *s = as->iommu_state; + DMAMap map; /* * Note: all the codes in this function has a assumption that IOVA @@ -2815,17 +2995,19 @@ static void vtd_address_space_unmap(VTDAddressSpace *as, IOMMUNotifier *n) VTD_PCI_FUNC(as->devfn), entry.iova, size); + map.iova = entry.iova; + map.size = entry.addr_mask; + iova_tree_remove(as->iova_tree, &map); + memory_region_notify_one(n, &entry); } static void vtd_address_space_unmap_all(IntelIOMMUState *s) { - IntelIOMMUNotifierNode *node; VTDAddressSpace *vtd_as; IOMMUNotifier *n; - QLIST_FOREACH(node, &s->notifiers_list, next) { - vtd_as = node->vtd_as; + QLIST_FOREACH(vtd_as, &s->vtd_as_with_notifiers, next) { IOMMU_NOTIFIER_FOREACH(n, &vtd_as->iommu) { vtd_address_space_unmap(vtd_as, n); } @@ -2857,8 +3039,19 @@ static void vtd_iommu_replay(IOMMUMemoryRegion *iommu_mr, IOMMUNotifier *n) PCI_FUNC(vtd_as->devfn), VTD_CONTEXT_ENTRY_DID(ce.hi), ce.hi, ce.lo); - vtd_page_walk(&ce, 0, ~0ULL, vtd_replay_hook, (void *)n, false, - s->aw_bits); + if (vtd_as_has_map_notifier(vtd_as)) { + /* This is required only for MAP typed notifiers */ + vtd_page_walk_info info = { + .hook_fn = vtd_replay_hook, + .private = (void *)n, + .notify_unmap = false, + .aw = s->aw_bits, + .as = vtd_as, + .domain_id = VTD_CONTEXT_ENTRY_DID(ce.hi), + }; + + vtd_page_walk(&ce, 0, ~0ULL, &info); + } } else { trace_vtd_replay_ce_invalid(bus_n, PCI_SLOT(vtd_as->devfn), PCI_FUNC(vtd_as->devfn)); @@ -2930,8 +3123,10 @@ static void vtd_init(IntelIOMMUState *s) s->cap |= VTD_CAP_CM; } - vtd_reset_context_cache(s); - vtd_reset_iotlb(s); + vtd_iommu_lock(s); + vtd_reset_context_cache_locked(s); + vtd_reset_iotlb_locked(s); + vtd_iommu_unlock(s); /* Define registers with default values and bit semantics */ vtd_define_long(s, DMAR_VER_REG, 0x10UL, 0, 0); @@ -3070,7 +3265,8 @@ static void vtd_realize(DeviceState *dev, Error **errp) return; } - QLIST_INIT(&s->notifiers_list); + QLIST_INIT(&s->vtd_as_with_notifiers); + qemu_mutex_init(&s->iommu_lock); memset(s->vtd_as_by_bus_num, 0, sizeof(s->vtd_as_by_bus_num)); memory_region_init_io(&s->csrmem, OBJECT(s), &vtd_mem_ops, s, "intel_iommu", DMAR_REG_SIZE); diff --git a/hw/i386/kvm/clock.c b/hw/i386/kvm/clock.c index 7dac319403..0bf1c60a06 100644 --- a/hw/i386/kvm/clock.c +++ b/hw/i386/kvm/clock.c @@ -26,7 +26,7 @@ #include "qapi/error.h" #include <linux/kvm.h> -#include <linux/kvm_para.h> +#include "standard-headers/asm-x86/kvm_para.h" #define TYPE_KVM_CLOCK "kvmclock" #define KVM_CLOCK(obj) OBJECT_CHECK(KVMClockState, (obj), TYPE_KVM_CLOCK) diff --git a/hw/i386/trace-events b/hw/i386/trace-events index 22d44648af..e14d06ec83 100644 --- a/hw/i386/trace-events +++ b/hw/i386/trace-events @@ -39,9 +39,10 @@ vtd_fault_disabled(void) "Fault processing disabled for context entry" vtd_replay_ce_valid(uint8_t bus, uint8_t dev, uint8_t fn, uint16_t domain, uint64_t hi, uint64_t lo) "replay valid context device %02"PRIx8":%02"PRIx8".%02"PRIx8" domain 0x%"PRIx16" hi 0x%"PRIx64" lo 0x%"PRIx64 vtd_replay_ce_invalid(uint8_t bus, uint8_t dev, uint8_t fn) "replay invalid context device %02"PRIx8":%02"PRIx8".%02"PRIx8 vtd_page_walk_level(uint64_t addr, uint32_t level, uint64_t start, uint64_t end) "walk (base=0x%"PRIx64", level=%"PRIu32") iova range 0x%"PRIx64" - 0x%"PRIx64 -vtd_page_walk_one(uint32_t level, uint64_t iova, uint64_t gpa, uint64_t mask, int perm) "detected page level 0x%"PRIx32" iova 0x%"PRIx64" -> gpa 0x%"PRIx64" mask 0x%"PRIx64" perm %d" +vtd_page_walk_one(uint16_t domain, uint64_t iova, uint64_t gpa, uint64_t mask, int perm) "domain 0x%"PRIu16" iova 0x%"PRIx64" -> gpa 0x%"PRIx64" mask 0x%"PRIx64" perm %d" +vtd_page_walk_one_skip_map(uint64_t iova, uint64_t mask, uint64_t translated) "iova 0x%"PRIx64" mask 0x%"PRIx64" translated 0x%"PRIx64 +vtd_page_walk_one_skip_unmap(uint64_t iova, uint64_t mask) "iova 0x%"PRIx64" mask 0x%"PRIx64 vtd_page_walk_skip_read(uint64_t iova, uint64_t next) "Page walk skip iova 0x%"PRIx64" - 0x%"PRIx64" due to unable to read" -vtd_page_walk_skip_perm(uint64_t iova, uint64_t next) "Page walk skip iova 0x%"PRIx64" - 0x%"PRIx64" due to perm empty" vtd_page_walk_skip_reserve(uint64_t iova, uint64_t next) "Page walk skip iova 0x%"PRIx64" - 0x%"PRIx64" due to rsrv set" vtd_switch_address_space(uint8_t bus, uint8_t slot, uint8_t fn, bool on) "Device %02x:%02x.%x switching address space (iommu enabled=%d)" vtd_as_unmap_whole(uint8_t bus, uint8_t slot, uint8_t fn, uint64_t iova, uint64_t size) "Device %02x:%02x.%x start 0x%"PRIx64" size 0x%"PRIx64 diff --git a/hw/i386/xen/xen-hvm.c b/hw/i386/xen/xen-hvm.c index caa563be3d..6ffa3c22cc 100644 --- a/hw/i386/xen/xen-hvm.c +++ b/hw/i386/xen/xen-hvm.c @@ -95,7 +95,8 @@ typedef struct XenIOState { CPUState **cpu_by_vcpu_id; /* the evtchn port for polling the notification, */ evtchn_port_t *ioreq_local_port; - /* evtchn local port for buffered io */ + /* evtchn remote and local ports for buffered io */ + evtchn_port_t bufioreq_remote_port; evtchn_port_t bufioreq_local_port; /* the evtchn fd for polling */ xenevtchn_handle *xce_handle; @@ -1236,12 +1237,52 @@ static void xen_wakeup_notifier(Notifier *notifier, void *data) xc_set_hvm_param(xen_xc, xen_domid, HVM_PARAM_ACPI_S_STATE, 0); } -void xen_hvm_init(PCMachineState *pcms, MemoryRegion **ram_memory) +static int xen_map_ioreq_server(XenIOState *state) { - int i, rc; xen_pfn_t ioreq_pfn; xen_pfn_t bufioreq_pfn; evtchn_port_t bufioreq_evtchn; + int rc; + + rc = xen_get_ioreq_server_info(xen_domid, state->ioservid, + &ioreq_pfn, &bufioreq_pfn, + &bufioreq_evtchn); + if (rc < 0) { + error_report("failed to get ioreq server info: error %d handle=%p", + errno, xen_xc); + return rc; + } + + DPRINTF("shared page at pfn %lx\n", ioreq_pfn); + DPRINTF("buffered io page at pfn %lx\n", bufioreq_pfn); + DPRINTF("buffered io evtchn is %x\n", bufioreq_evtchn); + + state->shared_page = xenforeignmemory_map(xen_fmem, xen_domid, + PROT_READ | PROT_WRITE, + 1, &ioreq_pfn, NULL); + if (state->shared_page == NULL) { + error_report("map shared IO page returned error %d handle=%p", + errno, xen_xc); + return -1; + } + + state->buffered_io_page = xenforeignmemory_map(xen_fmem, xen_domid, + PROT_READ | PROT_WRITE, + 1, &bufioreq_pfn, NULL); + if (state->buffered_io_page == NULL) { + error_report("map buffered IO page returned error %d", errno); + return -1; + } + + state->bufioreq_remote_port = bufioreq_evtchn; + + return 0; +} + +void xen_hvm_init(PCMachineState *pcms, MemoryRegion **ram_memory) +{ + int i, rc; + xen_pfn_t ioreq_pfn; XenIOState *state; state = g_malloc0(sizeof (XenIOState)); @@ -1269,25 +1310,8 @@ void xen_hvm_init(PCMachineState *pcms, MemoryRegion **ram_memory) state->wakeup.notify = xen_wakeup_notifier; qemu_register_wakeup_notifier(&state->wakeup); - rc = xen_get_ioreq_server_info(xen_domid, state->ioservid, - &ioreq_pfn, &bufioreq_pfn, - &bufioreq_evtchn); + rc = xen_map_ioreq_server(state); if (rc < 0) { - error_report("failed to get ioreq server info: error %d handle=%p", - errno, xen_xc); - goto err; - } - - DPRINTF("shared page at pfn %lx\n", ioreq_pfn); - DPRINTF("buffered io page at pfn %lx\n", bufioreq_pfn); - DPRINTF("buffered io evtchn is %x\n", bufioreq_evtchn); - - state->shared_page = xenforeignmemory_map(xen_fmem, xen_domid, - PROT_READ|PROT_WRITE, - 1, &ioreq_pfn, NULL); - if (state->shared_page == NULL) { - error_report("map shared IO page returned error %d handle=%p", - errno, xen_xc); goto err; } @@ -1308,14 +1332,6 @@ void xen_hvm_init(PCMachineState *pcms, MemoryRegion **ram_memory) goto err; } - state->buffered_io_page = xenforeignmemory_map(xen_fmem, xen_domid, - PROT_READ|PROT_WRITE, - 1, &bufioreq_pfn, NULL); - if (state->buffered_io_page == NULL) { - error_report("map buffered IO page returned error %d", errno); - goto err; - } - /* Note: cpus is empty at this point in init */ state->cpu_by_vcpu_id = g_malloc0(max_cpus * sizeof(CPUState *)); @@ -1340,7 +1356,7 @@ void xen_hvm_init(PCMachineState *pcms, MemoryRegion **ram_memory) } rc = xenevtchn_bind_interdomain(state->xce_handle, xen_domid, - bufioreq_evtchn); + state->bufioreq_remote_port); if (rc == -1) { error_report("buffered evtchn bind error %d", errno); goto err; diff --git a/hw/i386/xen/xen_pvdevice.c b/hw/i386/xen/xen_pvdevice.c index f748823658..a146f1883a 100644 --- a/hw/i386/xen/xen_pvdevice.c +++ b/hw/i386/xen/xen_pvdevice.c @@ -71,6 +71,16 @@ static const MemoryRegionOps xen_pv_mmio_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; +static const VMStateDescription vmstate_xen_pvdevice = { + .name = "xen-pvdevice", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_PCI_DEVICE(parent_obj, XenPVDevice), + VMSTATE_END_OF_LIST() + } +}; + static void xen_pv_realize(PCIDevice *pci_dev, Error **errp) { XenPVDevice *d = XEN_PV_DEVICE(pci_dev); @@ -120,6 +130,7 @@ static void xen_pv_class_init(ObjectClass *klass, void *data) k->class_id = PCI_CLASS_SYSTEM_OTHER; dc->desc = "Xen PV Device"; dc->props = xen_pv_props; + dc->vmsd = &vmstate_xen_pvdevice; } static const TypeInfo xen_pv_type_info = { diff --git a/hw/mem/nvdimm.c b/hw/mem/nvdimm.c index acb656b672..4087aca25e 100644 --- a/hw/mem/nvdimm.c +++ b/hw/mem/nvdimm.c @@ -89,7 +89,7 @@ static void nvdimm_set_unarmed(Object *obj, bool value, Error **errp) static void nvdimm_init(Object *obj) { - object_property_add(obj, NVDIMM_LABLE_SIZE_PROP, "int", + object_property_add(obj, NVDIMM_LABEL_SIZE_PROP, "int", nvdimm_get_label_size, nvdimm_set_label_size, NULL, NULL, NULL); object_property_add_bool(obj, NVDIMM_UNARMED_PROP, diff --git a/hw/net/xen_nic.c b/hw/net/xen_nic.c index 20c43a61b3..46a8dbfc90 100644 --- a/hw/net/xen_nic.c +++ b/hw/net/xen_nic.c @@ -160,9 +160,8 @@ static void net_tx_packets(struct XenNetDev *netdev) (txreq.flags & NETTXF_more_data) ? " more_data" : "", (txreq.flags & NETTXF_extra_info) ? " extra_info" : ""); - page = xengnttab_map_grant_ref(netdev->xendev.gnttabdev, - netdev->xendev.dom, - txreq.gref, PROT_READ); + page = xen_be_map_grant_ref(&netdev->xendev, txreq.gref, + PROT_READ); if (page == NULL) { xen_pv_printf(&netdev->xendev, 0, "error: tx gref dereference failed (%d)\n", @@ -183,7 +182,7 @@ static void net_tx_packets(struct XenNetDev *netdev) qemu_send_packet(qemu_get_queue(netdev->nic), page + txreq.offset, txreq.size); } - xengnttab_unmap(netdev->xendev.gnttabdev, page, 1); + xen_be_unmap_grant_ref(&netdev->xendev, page); net_tx_response(netdev, &txreq, NETIF_RSP_OKAY); } if (!netdev->tx_work) { @@ -254,9 +253,7 @@ static ssize_t net_rx_packet(NetClientState *nc, const uint8_t *buf, size_t size memcpy(&rxreq, RING_GET_REQUEST(&netdev->rx_ring, rc), sizeof(rxreq)); netdev->rx_ring.req_cons = ++rc; - page = xengnttab_map_grant_ref(netdev->xendev.gnttabdev, - netdev->xendev.dom, - rxreq.gref, PROT_WRITE); + page = xen_be_map_grant_ref(&netdev->xendev, rxreq.gref, PROT_WRITE); if (page == NULL) { xen_pv_printf(&netdev->xendev, 0, "error: rx gref dereference failed (%d)\n", @@ -265,7 +262,7 @@ static ssize_t net_rx_packet(NetClientState *nc, const uint8_t *buf, size_t size return -1; } memcpy(page + NET_IP_ALIGN, buf, size); - xengnttab_unmap(netdev->xendev.gnttabdev, page, 1); + xen_be_unmap_grant_ref(&netdev->xendev, page); net_rx_response(netdev, &rxreq, NETIF_RSP_OKAY, NET_IP_ALIGN, size, 0); return size; @@ -338,19 +335,17 @@ static int net_connect(struct XenDevice *xendev) return -1; } - netdev->txs = xengnttab_map_grant_ref(netdev->xendev.gnttabdev, - netdev->xendev.dom, - netdev->tx_ring_ref, - PROT_READ | PROT_WRITE); + netdev->txs = xen_be_map_grant_ref(&netdev->xendev, + netdev->tx_ring_ref, + PROT_READ | PROT_WRITE); if (!netdev->txs) { return -1; } - netdev->rxs = xengnttab_map_grant_ref(netdev->xendev.gnttabdev, - netdev->xendev.dom, - netdev->rx_ring_ref, - PROT_READ | PROT_WRITE); + netdev->rxs = xen_be_map_grant_ref(&netdev->xendev, + netdev->rx_ring_ref, + PROT_READ | PROT_WRITE); if (!netdev->rxs) { - xengnttab_unmap(netdev->xendev.gnttabdev, netdev->txs, 1); + xen_be_unmap_grant_ref(&netdev->xendev, netdev->txs); netdev->txs = NULL; return -1; } @@ -375,11 +370,11 @@ static void net_disconnect(struct XenDevice *xendev) xen_pv_unbind_evtchn(&netdev->xendev); if (netdev->txs) { - xengnttab_unmap(netdev->xendev.gnttabdev, netdev->txs, 1); + xen_be_unmap_grant_ref(&netdev->xendev, netdev->txs); netdev->txs = NULL; } if (netdev->rxs) { - xengnttab_unmap(netdev->xendev.gnttabdev, netdev->rxs, 1); + xen_be_unmap_grant_ref(&netdev->xendev, netdev->rxs); netdev->rxs = NULL; } } diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c index a36a1195e4..02f9576588 100644 --- a/hw/pci-host/q35.c +++ b/hw/pci-host/q35.c @@ -535,13 +535,15 @@ static void mch_realize(PCIDevice *d, Error **errp) /* if *disabled* show SMRAM to all CPUs */ memory_region_init_alias(&mch->smram_region, OBJECT(mch), "smram-region", - mch->pci_address_space, 0xa0000, 0x20000); - memory_region_add_subregion_overlap(mch->system_memory, 0xa0000, + mch->pci_address_space, MCH_HOST_BRIDGE_SMRAM_C_BASE, + MCH_HOST_BRIDGE_SMRAM_C_SIZE); + memory_region_add_subregion_overlap(mch->system_memory, MCH_HOST_BRIDGE_SMRAM_C_BASE, &mch->smram_region, 1); memory_region_set_enabled(&mch->smram_region, true); memory_region_init_alias(&mch->open_high_smram, OBJECT(mch), "smram-open-high", - mch->ram_memory, 0xa0000, 0x20000); + mch->ram_memory, MCH_HOST_BRIDGE_SMRAM_C_BASE, + MCH_HOST_BRIDGE_SMRAM_C_SIZE); memory_region_add_subregion_overlap(mch->system_memory, 0xfeda0000, &mch->open_high_smram, 1); memory_region_set_enabled(&mch->open_high_smram, false); @@ -550,11 +552,14 @@ static void mch_realize(PCIDevice *d, Error **errp) memory_region_init(&mch->smram, OBJECT(mch), "smram", 1ull << 32); memory_region_set_enabled(&mch->smram, true); memory_region_init_alias(&mch->low_smram, OBJECT(mch), "smram-low", - mch->ram_memory, 0xa0000, 0x20000); + mch->ram_memory, MCH_HOST_BRIDGE_SMRAM_C_BASE, + MCH_HOST_BRIDGE_SMRAM_C_SIZE); memory_region_set_enabled(&mch->low_smram, true); - memory_region_add_subregion(&mch->smram, 0xa0000, &mch->low_smram); + memory_region_add_subregion(&mch->smram, MCH_HOST_BRIDGE_SMRAM_C_BASE, + &mch->low_smram); memory_region_init_alias(&mch->high_smram, OBJECT(mch), "smram-high", - mch->ram_memory, 0xa0000, 0x20000); + mch->ram_memory, MCH_HOST_BRIDGE_SMRAM_C_BASE, + MCH_HOST_BRIDGE_SMRAM_C_SIZE); memory_region_set_enabled(&mch->high_smram, true); memory_region_add_subregion(&mch->smram, 0xfeda0000, &mch->high_smram); diff --git a/hw/usb/xen-usb.c b/hw/usb/xen-usb.c index b3a90c0e68..5b2e21ed18 100644 --- a/hw/usb/xen-usb.c +++ b/hw/usb/xen-usb.c @@ -173,8 +173,9 @@ static int usbback_gnttab_map(struct usbback_req *usbback_req) for (i = 0; i < usbback_req->nr_buffer_segs; i++) { ref[i] = usbback_req->req.seg[i].gref; } - usbback_req->buffer = xengnttab_map_domain_grant_refs(xendev->gnttabdev, - usbback_req->nr_buffer_segs, xendev->dom, ref, prot); + usbback_req->buffer = + xen_be_map_grant_refs(xendev, ref, usbback_req->nr_buffer_segs, + prot); if (!usbback_req->buffer) { return -ENOMEM; @@ -206,8 +207,9 @@ static int usbback_gnttab_map(struct usbback_req *usbback_req) for (i = 0; i < usbback_req->nr_extra_segs; i++) { ref[i] = usbback_req->req.seg[i + usbback_req->req.nr_buffer_segs].gref; } - usbback_req->isoc_buffer = xengnttab_map_domain_grant_refs( - xendev->gnttabdev, usbback_req->nr_extra_segs, xendev->dom, ref, prot); + usbback_req->isoc_buffer = + xen_be_map_grant_refs(xendev, ref, usbback_req->nr_extra_segs, + prot); if (!usbback_req->isoc_buffer) { return -ENOMEM; @@ -291,14 +293,14 @@ static void usbback_do_response(struct usbback_req *usbback_req, int32_t status, } if (usbback_req->buffer) { - xengnttab_unmap(xendev->gnttabdev, usbback_req->buffer, - usbback_req->nr_buffer_segs); + xen_be_unmap_grant_refs(xendev, usbback_req->buffer, + usbback_req->nr_buffer_segs); usbback_req->buffer = NULL; } if (usbback_req->isoc_buffer) { - xengnttab_unmap(xendev->gnttabdev, usbback_req->isoc_buffer, - usbback_req->nr_extra_segs); + xen_be_unmap_grant_refs(xendev, usbback_req->isoc_buffer, + usbback_req->nr_extra_segs); usbback_req->isoc_buffer = NULL; } @@ -834,11 +836,11 @@ static void usbback_disconnect(struct XenDevice *xendev) xen_pv_unbind_evtchn(xendev); if (usbif->urb_sring) { - xengnttab_unmap(xendev->gnttabdev, usbif->urb_sring, 1); + xen_be_unmap_grant_ref(xendev, usbif->urb_sring); usbif->urb_sring = NULL; } if (usbif->conn_sring) { - xengnttab_unmap(xendev->gnttabdev, usbif->conn_sring, 1); + xen_be_unmap_grant_ref(xendev, usbif->conn_sring); usbif->conn_sring = NULL; } @@ -877,12 +879,10 @@ static int usbback_connect(struct XenDevice *xendev) return -1; } - usbif->urb_sring = xengnttab_map_grant_ref(xendev->gnttabdev, xendev->dom, - urb_ring_ref, - PROT_READ | PROT_WRITE); - usbif->conn_sring = xengnttab_map_grant_ref(xendev->gnttabdev, xendev->dom, - conn_ring_ref, - PROT_READ | PROT_WRITE); + usbif->urb_sring = xen_be_map_grant_ref(xendev, urb_ring_ref, + PROT_READ | PROT_WRITE); + usbif->conn_sring = xen_be_map_grant_ref(xendev, conn_ring_ref, + PROT_READ | PROT_WRITE); if (!usbif->urb_sring || !usbif->conn_sring) { xen_pv_printf(xendev, 0, "error mapping rings\n"); usbback_disconnect(xendev); @@ -1024,10 +1024,7 @@ static void usbback_alloc(struct XenDevice *xendev) /* max_grants: for each request and for the rings (request and connect). */ max_grants = USBIF_MAX_SEGMENTS_PER_REQUEST * USB_URB_RING_SIZE + 2; - if (xengnttab_set_max_grants(xendev->gnttabdev, max_grants) < 0) { - xen_pv_printf(xendev, 0, "xengnttab_set_max_grants failed: %s\n", - strerror(errno)); - } + xen_be_set_max_grant_refs(xendev, max_grants); } static int usbback_free(struct XenDevice *xendev) diff --git a/hw/virtio/trace-events b/hw/virtio/trace-events index 1422ff03ab..07bcbe9e85 100644 --- a/hw/virtio/trace-events +++ b/hw/virtio/trace-events @@ -6,6 +6,7 @@ vhost_region_add_section(const char *name, uint64_t gpa, uint64_t size, uint64_t vhost_region_add_section_merge(const char *name, uint64_t new_size, uint64_t gpa, uint64_t owr) "%s: size: 0x%"PRIx64 " gpa: 0x%"PRIx64 " owr: 0x%"PRIx64 vhost_region_add_section_aligned(const char *name, uint64_t gpa, uint64_t size, uint64_t host) "%s: 0x%"PRIx64"+0x%"PRIx64" @ 0x%"PRIx64 vhost_section(const char *name, int r) "%s:%d" +vhost_iotlb_miss(void *dev, int step) "%p step %d" # hw/virtio/vhost-user.c vhost_user_postcopy_end_entry(void) "" diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index 38da8692bb..ca554d4ff1 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -852,14 +852,44 @@ static void slave_read(void *opaque) VhostUserHeader hdr = { 0, }; VhostUserPayload payload = { 0, }; int size, ret = 0; + struct iovec iov; + struct msghdr msgh; + int fd = -1; + char control[CMSG_SPACE(sizeof(fd))]; + struct cmsghdr *cmsg; + size_t fdsize; + + memset(&msgh, 0, sizeof(msgh)); + msgh.msg_iov = &iov; + msgh.msg_iovlen = 1; + msgh.msg_control = control; + msgh.msg_controllen = sizeof(control); /* Read header */ - size = read(u->slave_fd, &hdr, VHOST_USER_HDR_SIZE); + iov.iov_base = &hdr; + iov.iov_len = VHOST_USER_HDR_SIZE; + + size = recvmsg(u->slave_fd, &msgh, 0); if (size != VHOST_USER_HDR_SIZE) { error_report("Failed to read from slave."); goto err; } + if (msgh.msg_flags & MSG_CTRUNC) { + error_report("Truncated message."); + goto err; + } + + for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL; + cmsg = CMSG_NXTHDR(&msgh, cmsg)) { + if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SCM_RIGHTS) { + fdsize = cmsg->cmsg_len - CMSG_LEN(0); + memcpy(&fd, CMSG_DATA(cmsg), fdsize); + break; + } + } + if (hdr.size > VHOST_USER_PAYLOAD_SIZE) { error_report("Failed to read msg header." " Size %d exceeds the maximum %zu.", hdr.size, @@ -883,9 +913,15 @@ static void slave_read(void *opaque) break; default: error_report("Received unexpected msg type."); + if (fd != -1) { + close(fd); + } ret = -EINVAL; } + /* Message handlers need to make sure that fd will be consumed. */ + fd = -1; + /* * REPLY_ACK feature handling. Other reply types has to be managed * directly in their request handlers. @@ -918,6 +954,9 @@ err: qemu_set_fd_handler(u->slave_fd, NULL, NULL, NULL); close(u->slave_fd); u->slave_fd = -1; + if (fd != -1) { + close(fd); + } return; } @@ -1076,7 +1115,7 @@ static int vhost_user_postcopy_advise(struct vhost_dev *dev, Error **errp) error_setg(errp, "%s: Failed to get ufd", __func__); return -1; } - fcntl(ufd, F_SETFL, O_NONBLOCK); + qemu_set_nonblock(ufd); /* register ufd with userfault thread */ u->postcopy_fd.fd = ufd; @@ -1316,7 +1355,7 @@ static bool vhost_user_requires_shm_log(struct vhost_dev *dev) static int vhost_user_migration_done(struct vhost_dev *dev, char* mac_addr) { - VhostUserMsg msg = { 0 }; + VhostUserMsg msg = { }; assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_USER); diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index 9d5850a7d7..b08290036d 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -894,12 +894,15 @@ int vhost_device_iotlb_miss(struct vhost_dev *dev, uint64_t iova, int write) rcu_read_lock(); + trace_vhost_iotlb_miss(dev, 1); + iotlb = address_space_get_iotlb_entry(dev->vdev->dma_as, iova, write); if (iotlb.target_as != NULL) { ret = vhost_memory_region_lookup(dev, iotlb.translated_addr, &uaddr, &len); if (ret) { + trace_vhost_iotlb_miss(dev, 3); error_report("Fail to lookup the translated address " "%"PRIx64, iotlb.translated_addr); goto out; @@ -911,10 +914,14 @@ int vhost_device_iotlb_miss(struct vhost_dev *dev, uint64_t iova, int write) ret = vhost_backend_update_device_iotlb(dev, iova, uaddr, len, iotlb.perm); if (ret) { + trace_vhost_iotlb_miss(dev, 4); error_report("Fail to update device iotlb"); goto out; } } + + trace_vhost_iotlb_miss(dev, 2); + out: rcu_read_unlock(); diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c index f456cea2e7..1f7a87f094 100644 --- a/hw/virtio/virtio-balloon.c +++ b/hw/virtio/virtio-balloon.c @@ -52,6 +52,8 @@ static const char *balloon_stat_names[] = { [VIRTIO_BALLOON_S_MEMTOT] = "stat-total-memory", [VIRTIO_BALLOON_S_AVAIL] = "stat-available-memory", [VIRTIO_BALLOON_S_CACHES] = "stat-disk-caches", + [VIRTIO_BALLOON_S_HTLB_PGALLOC] = "stat-htlb-pgalloc", + [VIRTIO_BALLOON_S_HTLB_PGFAIL] = "stat-htlb-pgfail", [VIRTIO_BALLOON_S_NR] = NULL }; diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index 1e8ab7bbc5..5eb0c323ca 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -1037,6 +1037,27 @@ assign_error: return r; } +static int virtio_pci_set_host_notifier_mr(DeviceState *d, int n, + MemoryRegion *mr, bool assign) +{ + VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d); + int offset; + + if (n >= VIRTIO_QUEUE_MAX || !virtio_pci_modern(proxy) || + virtio_pci_queue_mem_mult(proxy) != memory_region_size(mr)) { + return -1; + } + + if (assign) { + offset = virtio_pci_queue_mem_mult(proxy) * n; + memory_region_add_subregion_overlap(&proxy->notify.mr, offset, mr, 1); + } else { + memory_region_del_subregion(&proxy->notify.mr, mr); + } + + return 0; +} + static void virtio_pci_vmstate_change(DeviceState *d, bool running) { VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d); @@ -2652,6 +2673,7 @@ static void virtio_pci_bus_class_init(ObjectClass *klass, void *data) k->has_extra_state = virtio_pci_has_extra_state; k->query_guest_notifiers = virtio_pci_query_guest_notifiers; k->set_guest_notifiers = virtio_pci_set_guest_notifiers; + k->set_host_notifier_mr = virtio_pci_set_host_notifier_mr; k->vmstate_change = virtio_pci_vmstate_change; k->pre_plugged = virtio_pci_pre_plugged; k->device_plugged = virtio_pci_device_plugged; diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 006d3d1148..1debb0147b 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -2454,6 +2454,19 @@ EventNotifier *virtio_queue_get_host_notifier(VirtQueue *vq) return &vq->host_notifier; } +int virtio_queue_set_host_notifier_mr(VirtIODevice *vdev, int n, + MemoryRegion *mr, bool assign) +{ + BusState *qbus = qdev_get_parent_bus(DEVICE(vdev)); + VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); + + if (k->set_host_notifier_mr) { + return k->set_host_notifier_mr(qbus->parent, n, mr, assign); + } + + return -1; +} + void virtio_device_set_child_bus_name(VirtIODevice *vdev, char *bus_name) { g_free(vdev->bus_name); diff --git a/hw/xen/xen_backend.c b/hw/xen/xen_backend.c index 7445b506ac..9a8e8771ec 100644 --- a/hw/xen/xen_backend.c +++ b/hw/xen/xen_backend.c @@ -44,9 +44,9 @@ BusState *xen_sysbus; /* public */ struct xs_handle *xenstore = NULL; const char *xen_protocol; -bool xen_feature_grant_copy; /* private */ +static bool xen_feature_grant_copy; static int debug; int xenstore_write_be_str(struct XenDevice *xendev, const char *node, const char *val) @@ -106,6 +106,156 @@ int xen_be_set_state(struct XenDevice *xendev, enum xenbus_state state) return 0; } +void xen_be_set_max_grant_refs(struct XenDevice *xendev, + unsigned int nr_refs) +{ + assert(xendev->ops->flags & DEVOPS_FLAG_NEED_GNTDEV); + + if (xengnttab_set_max_grants(xendev->gnttabdev, nr_refs)) { + xen_pv_printf(xendev, 0, "xengnttab_set_max_grants failed: %s\n", + strerror(errno)); + } +} + +void *xen_be_map_grant_refs(struct XenDevice *xendev, uint32_t *refs, + unsigned int nr_refs, int prot) +{ + void *ptr; + + assert(xendev->ops->flags & DEVOPS_FLAG_NEED_GNTDEV); + + ptr = xengnttab_map_domain_grant_refs(xendev->gnttabdev, nr_refs, + xen_domid, refs, prot); + if (!ptr) { + xen_pv_printf(xendev, 0, + "xengnttab_map_domain_grant_refs failed: %s\n", + strerror(errno)); + } + + return ptr; +} + +void xen_be_unmap_grant_refs(struct XenDevice *xendev, void *ptr, + unsigned int nr_refs) +{ + assert(xendev->ops->flags & DEVOPS_FLAG_NEED_GNTDEV); + + if (xengnttab_unmap(xendev->gnttabdev, ptr, nr_refs)) { + xen_pv_printf(xendev, 0, "xengnttab_unmap failed: %s\n", + strerror(errno)); + } +} + +static int compat_copy_grant_refs(struct XenDevice *xendev, + bool to_domain, + XenGrantCopySegment segs[], + unsigned int nr_segs) +{ + uint32_t *refs = g_new(uint32_t, nr_segs); + int prot = to_domain ? PROT_WRITE : PROT_READ; + void *pages; + unsigned int i; + + for (i = 0; i < nr_segs; i++) { + XenGrantCopySegment *seg = &segs[i]; + + refs[i] = to_domain ? + seg->dest.foreign.ref : seg->source.foreign.ref; + } + + pages = xengnttab_map_domain_grant_refs(xendev->gnttabdev, nr_segs, + xen_domid, refs, prot); + if (!pages) { + xen_pv_printf(xendev, 0, + "xengnttab_map_domain_grant_refs failed: %s\n", + strerror(errno)); + g_free(refs); + return -1; + } + + for (i = 0; i < nr_segs; i++) { + XenGrantCopySegment *seg = &segs[i]; + void *page = pages + (i * XC_PAGE_SIZE); + + if (to_domain) { + memcpy(page + seg->dest.foreign.offset, seg->source.virt, + seg->len); + } else { + memcpy(seg->dest.virt, page + seg->source.foreign.offset, + seg->len); + } + } + + if (xengnttab_unmap(xendev->gnttabdev, pages, nr_segs)) { + xen_pv_printf(xendev, 0, "xengnttab_unmap failed: %s\n", + strerror(errno)); + } + + g_free(refs); + return 0; +} + +int xen_be_copy_grant_refs(struct XenDevice *xendev, + bool to_domain, + XenGrantCopySegment segs[], + unsigned int nr_segs) +{ + xengnttab_grant_copy_segment_t *xengnttab_segs; + unsigned int i; + int rc; + + assert(xendev->ops->flags & DEVOPS_FLAG_NEED_GNTDEV); + + if (!xen_feature_grant_copy) { + return compat_copy_grant_refs(xendev, to_domain, segs, nr_segs); + } + + xengnttab_segs = g_new0(xengnttab_grant_copy_segment_t, nr_segs); + + for (i = 0; i < nr_segs; i++) { + XenGrantCopySegment *seg = &segs[i]; + xengnttab_grant_copy_segment_t *xengnttab_seg = &xengnttab_segs[i]; + + if (to_domain) { + xengnttab_seg->flags = GNTCOPY_dest_gref; + xengnttab_seg->dest.foreign.domid = xen_domid; + xengnttab_seg->dest.foreign.ref = seg->dest.foreign.ref; + xengnttab_seg->dest.foreign.offset = seg->dest.foreign.offset; + xengnttab_seg->source.virt = seg->source.virt; + } else { + xengnttab_seg->flags = GNTCOPY_source_gref; + xengnttab_seg->source.foreign.domid = xen_domid; + xengnttab_seg->source.foreign.ref = seg->source.foreign.ref; + xengnttab_seg->source.foreign.offset = + seg->source.foreign.offset; + xengnttab_seg->dest.virt = seg->dest.virt; + } + + xengnttab_seg->len = seg->len; + } + + rc = xengnttab_grant_copy(xendev->gnttabdev, nr_segs, xengnttab_segs); + + if (rc) { + xen_pv_printf(xendev, 0, "xengnttab_copy failed: %s\n", + strerror(errno)); + } + + for (i = 0; i < nr_segs; i++) { + xengnttab_grant_copy_segment_t *xengnttab_seg = + &xengnttab_segs[i]; + + if (xengnttab_seg->status != GNTST_okay) { + xen_pv_printf(xendev, 0, "segment[%u] status: %d\n", i, + xengnttab_seg->status); + rc = -1; + } + } + + g_free(xengnttab_segs); + return rc; +} + /* * get xen backend device, allocate a new one if it doesn't exist. */ @@ -149,18 +299,6 @@ static struct XenDevice *xen_be_get_xendev(const char *type, int dom, int dev, } qemu_set_cloexec(xenevtchn_fd(xendev->evtchndev)); - if (ops->flags & DEVOPS_FLAG_NEED_GNTDEV) { - xendev->gnttabdev = xengnttab_open(NULL, 0); - if (xendev->gnttabdev == NULL) { - xen_pv_printf(NULL, 0, "can't open gnttab device\n"); - xenevtchn_close(xendev->evtchndev); - qdev_unplug(DEVICE(xendev), NULL); - return NULL; - } - } else { - xendev->gnttabdev = NULL; - } - xen_pv_insert_xendev(xendev); if (xendev->ops->alloc) { @@ -322,6 +460,16 @@ static int xen_be_try_initialise(struct XenDevice *xendev) } } + if (xendev->ops->flags & DEVOPS_FLAG_NEED_GNTDEV) { + xendev->gnttabdev = xengnttab_open(NULL, 0); + if (xendev->gnttabdev == NULL) { + xen_pv_printf(NULL, 0, "can't open gnttab device\n"); + return -1; + } + } else { + xendev->gnttabdev = NULL; + } + if (xendev->ops->initialise) { rc = xendev->ops->initialise(xendev); } @@ -369,6 +517,10 @@ static void xen_be_disconnect(struct XenDevice *xendev, enum xenbus_state state) xendev->ops->disconnect) { xendev->ops->disconnect(xendev); } + if (xendev->gnttabdev) { + xengnttab_close(xendev->gnttabdev); + xendev->gnttabdev = NULL; + } if (xendev->be_state != state) { xen_be_set_state(xendev, state); } diff --git a/hw/xen/xen_pt.c b/hw/xen/xen_pt.c index 9b7a960de1..e5a6eff44f 100644 --- a/hw/xen/xen_pt.c +++ b/hw/xen/xen_pt.c @@ -907,7 +907,7 @@ out: } } - memory_listener_register(&s->memory_listener, &s->dev.bus_master_as); + memory_listener_register(&s->memory_listener, &address_space_memory); memory_listener_register(&s->io_listener, &address_space_io); s->listener_set = true; XEN_PT_LOG(d, diff --git a/hw/xen/xen_pt_config_init.c b/hw/xen/xen_pt_config_init.c index a3ce33e78b..aee31c62bb 100644 --- a/hw/xen/xen_pt_config_init.c +++ b/hw/xen/xen_pt_config_init.c @@ -504,6 +504,8 @@ static int xen_pt_bar_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, bar_ro_mask = XEN_PT_BAR_IO_RO_MASK | (r_size - 1); break; case XEN_PT_BAR_FLAG_UPPER: + assert(index > 0); + r_size = d->io_regions[index - 1].size >> 32; bar_emu_mask = XEN_PT_BAR_ALLF; bar_ro_mask = r_size ? r_size - 1 : 0; break; |