[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH 4/4] vm_event: Add support for multi-page ring buffer
In high throughput introspection scenarios where lots of monitor vm_events are generated, the ring buffer can fill up before the monitor application gets a chance to handle all the requests thus blocking other vcpus which will have to wait for a slot to become available. This patch adds support for extending the ring buffer by allocating a number of pages from domheap and mapping them to the monitor application's domain using the foreignmemory_map_resource interface. Unlike the current implementation, the ring buffer pages are not part of the introspected DomU, so they will not be reclaimed when the monitor is disabled. Signed-off-by: Petre Pircalabu <ppircalabu@xxxxxxxxxxxxxxx> --- tools/libxc/include/xenctrl.h | 2 + tools/libxc/xc_monitor.c | 7 + tools/libxc/xc_private.h | 3 + tools/libxc/xc_vm_event.c | 49 +++++++ tools/tests/xen-access/xen-access.c | 33 +++-- xen/arch/x86/domain_page.c | 3 +- xen/arch/x86/mm.c | 14 ++ xen/common/vm_event.c | 258 +++++++++++++++++++++++++++--------- xen/include/public/domctl.h | 1 + xen/include/public/memory.h | 1 + xen/include/xen/sched.h | 5 +- xen/include/xen/vm_event.h | 4 + 12 files changed, 305 insertions(+), 75 deletions(-) diff --git a/tools/libxc/include/xenctrl.h b/tools/libxc/include/xenctrl.h index bb75bcc..4f91ee9 100644 --- a/tools/libxc/include/xenctrl.h +++ b/tools/libxc/include/xenctrl.h @@ -2005,6 +2005,8 @@ int xc_get_mem_access(xc_interface *xch, uint32_t domain_id, * Caller has to unmap this page when done. */ void *xc_monitor_enable(xc_interface *xch, uint32_t domain_id, uint32_t *port); +void *xc_monitor_enable_ex(xc_interface *xch, uint32_t domain_id, + int order, uint32_t *port); int xc_monitor_disable(xc_interface *xch, uint32_t domain_id); int xc_monitor_resume(xc_interface *xch, uint32_t domain_id); /* diff --git a/tools/libxc/xc_monitor.c b/tools/libxc/xc_monitor.c index 15e6a0e..5188835 100644 --- a/tools/libxc/xc_monitor.c +++ b/tools/libxc/xc_monitor.c @@ -28,6 +28,13 @@ void *xc_monitor_enable(xc_interface *xch, uint32_t domain_id, uint32_t *port) port); } +void *xc_monitor_enable_ex(xc_interface *xch, uint32_t domain_id, int order, + uint32_t *port) +{ + return xc_vm_event_enable_ex(xch, domain_id, XEN_VM_EVENT_TYPE_MONITOR, + order, port); +} + int xc_monitor_disable(xc_interface *xch, uint32_t domain_id) { return xc_vm_event_control(xch, domain_id, diff --git a/tools/libxc/xc_private.h b/tools/libxc/xc_private.h index be22986..03d9460 100644 --- a/tools/libxc/xc_private.h +++ b/tools/libxc/xc_private.h @@ -436,6 +436,9 @@ int xc_vm_event_control(xc_interface *xch, uint32_t domain_id, unsigned int op, void *xc_vm_event_enable(xc_interface *xch, uint32_t domain_id, int type, uint32_t *port); +void *xc_vm_event_enable_ex(xc_interface *xch, uint32_t domain_id, int type, + int order, uint32_t *port); + int do_dm_op(xc_interface *xch, uint32_t domid, unsigned int nr_bufs, ...); #endif /* __XC_PRIVATE_H__ */ diff --git a/tools/libxc/xc_vm_event.c b/tools/libxc/xc_vm_event.c index de37ca5..216bbe2 100644 --- a/tools/libxc/xc_vm_event.c +++ b/tools/libxc/xc_vm_event.c @@ -21,6 +21,7 @@ */ #include "xc_private.h" +#include "xenforeignmemory.h" int xc_vm_event_control(xc_interface *xch, uint32_t domain_id, unsigned int op, unsigned int mode, uint32_t *port) @@ -184,6 +185,54 @@ void *xc_vm_event_enable(xc_interface *xch, uint32_t domain_id, int type, return ring_page; } +void *xc_vm_event_enable_ex(xc_interface *xch, uint32_t domain_id, int type, + int order, uint32_t *port) +{ + xenforeignmemory_resource_handle *fres = NULL; + int saved_errno; + void *ring_buffer = NULL; + + if ( !port ) + { + errno = EINVAL; + return NULL; + } + + /* Pause the domain for ring page setup */ + if ( xc_domain_pause(xch, domain_id) ) + { + PERROR("Unable to pause domain\n"); + return NULL; + } + + fres = xenforeignmemory_map_resource(xch->fmem, domain_id, + XENMEM_resource_vm_event, type, 0, + order, &ring_buffer, + PROT_READ | PROT_WRITE, 0); + if ( !fres ) + { + PERROR("Unable to map vm_event ring pages resource\n"); + goto out; + } + + if ( xc_vm_event_control(xch, domain_id, XEN_VM_EVENT_GET_PORT, type, port) ) + PERROR("Unable to get vm_event channel port\n"); + +out: + saved_errno = errno; + if ( xc_domain_unpause(xch, domain_id) != 0 ) + { + if (fres) + saved_errno = errno; + PERROR("Unable to unpause domain"); + } + + free(fres); + errno = saved_errno; + return ring_buffer; +} + + /* * Local variables: * mode: C diff --git a/tools/tests/xen-access/xen-access.c b/tools/tests/xen-access/xen-access.c index 6aaee16..f4c4eda 100644 --- a/tools/tests/xen-access/xen-access.c +++ b/tools/tests/xen-access/xen-access.c @@ -68,7 +68,8 @@ typedef struct vm_event { int port; vm_event_back_ring_t back_ring; uint32_t evtchn_port; - void *ring_page; + void *ring_buffer; + unsigned int ring_page_count; } vm_event_t; typedef struct xenaccess { @@ -136,8 +137,9 @@ int xenaccess_teardown(xc_interface *xch, xenaccess_t *xenaccess) return 0; /* Tear down domain xenaccess in Xen */ - if ( xenaccess->vm_event.ring_page ) - munmap(xenaccess->vm_event.ring_page, XC_PAGE_SIZE); + if ( xenaccess->vm_event.ring_buffer ) + munmap(xenaccess->vm_event.ring_buffer, + xenaccess->vm_event.ring_page_count * XC_PAGE_SIZE ); if ( mem_access_enable ) { @@ -210,12 +212,25 @@ xenaccess_t *xenaccess_init(xc_interface **xch_r, domid_t domain_id) /* Set domain id */ xenaccess->vm_event.domain_id = domain_id; - /* Enable mem_access */ - xenaccess->vm_event.ring_page = + /* Ring buffer page count */ + xenaccess->vm_event.ring_page_count = 2; + + xenaccess->vm_event.ring_buffer = xc_monitor_enable_ex( + xenaccess->xc_handle, + xenaccess->vm_event.domain_id, + xenaccess->vm_event.ring_page_count, + &xenaccess->vm_event.evtchn_port); + + if (xenaccess->vm_event.ring_buffer == NULL && errno == EOPNOTSUPP) + { + xenaccess->vm_event.ring_page_count = 1; + xenaccess->vm_event.ring_buffer = xc_monitor_enable(xenaccess->xc_handle, xenaccess->vm_event.domain_id, &xenaccess->vm_event.evtchn_port); - if ( xenaccess->vm_event.ring_page == NULL ) + } + + if ( xenaccess->vm_event.ring_buffer == NULL ) { switch ( errno ) { case EBUSY: @@ -254,10 +269,10 @@ xenaccess_t *xenaccess_init(xc_interface **xch_r, domid_t domain_id) xenaccess->vm_event.port = rc; /* Initialise ring */ - SHARED_RING_INIT((vm_event_sring_t *)xenaccess->vm_event.ring_page); + SHARED_RING_INIT((vm_event_sring_t *)xenaccess->vm_event.ring_buffer); BACK_RING_INIT(&xenaccess->vm_event.back_ring, - (vm_event_sring_t *)xenaccess->vm_event.ring_page, - XC_PAGE_SIZE); + (vm_event_sring_t *)xenaccess->vm_event.ring_buffer, + XC_PAGE_SIZE * xenaccess->vm_event.ring_page_count ); /* Get max_gpfn */ rc = xc_domain_maximum_gpfn(xenaccess->xc_handle, diff --git a/xen/arch/x86/domain_page.c b/xen/arch/x86/domain_page.c index 0d23e52..2a9cbf3 100644 --- a/xen/arch/x86/domain_page.c +++ b/xen/arch/x86/domain_page.c @@ -331,10 +331,9 @@ void *__map_domain_pages_global(const struct page_info *pg, unsigned int nr) { mfn_t mfn[nr]; int i; - struct page_info *cur_pg = (struct page_info *)&pg[0]; for (i = 0; i < nr; i++) - mfn[i] = page_to_mfn(cur_pg++); + mfn[i] = page_to_mfn(pg++); return map_domain_pages_global(mfn, nr); } diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c index baea2f5..bec09d0 100644 --- a/xen/arch/x86/mm.c +++ b/xen/arch/x86/mm.c @@ -103,6 +103,7 @@ #include <xen/efi.h> #include <xen/grant_table.h> #include <xen/hypercall.h> +#include <xen/vm_event.h> #include <asm/paging.h> #include <asm/shadow.h> #include <asm/page.h> @@ -4415,6 +4416,19 @@ int arch_acquire_resource(struct domain *d, unsigned int type, } #endif + case XENMEM_resource_vm_event: + { + rc = vm_event_get_ring_frames(d, id, frame, nr_frames, mfn_list); + if ( rc ) + break; + /* + * The frames will have been assigned to the domain that created + * the ioreq server. + */ + *flags |= XENMEM_rsrc_acq_caller_owned; + break; + } + default: rc = -EOPNOTSUPP; break; diff --git a/xen/common/vm_event.c b/xen/common/vm_event.c index 4793aac..faece3c 100644 --- a/xen/common/vm_event.c +++ b/xen/common/vm_event.c @@ -39,16 +39,66 @@ #define vm_event_ring_lock(_ved) spin_lock(&(_ved)->ring_lock) #define vm_event_ring_unlock(_ved) spin_unlock(&(_ved)->ring_lock) +#define XEN_VM_EVENT_ALLOC_FROM_DOMHEAP 0xFFFFFFFF + +static int vm_event_alloc_ring(struct domain *d, struct vm_event_domain *ved) +{ + struct page_info *page; + void *va = NULL; + int i, rc = -ENOMEM; + + page = alloc_domheap_pages(d, ved->ring_order, MEMF_no_refcount); + if ( !page ) + return -ENOMEM; + + for ( i = 0; i < (1 << ved->ring_order); i++ ) + if ( !get_page_type(&page[i], PGT_writable_page) ) + { + rc = -EINVAL; + goto fail; + } + + va = __map_domain_pages_global(page, (1 << ved->ring_order)); + if ( !va ) + goto fail; + + memset(va, 0, PAGE_SIZE << ved->ring_order); + + ved->ring_buffer = va; + ved->ring_pg_struct = page; + return 0; + +fail: + i--; + for ( ; i >= 0; i-- ) + put_page_type(&page[i]); + free_domheap_pages(page, ved->ring_order); + + return rc; + } + +static void vm_event_destroy_ring(struct vm_event_domain *ved) +{ + if ( ved->ring_buffer ) + { + int i; + + unmap_domain_page_global(ved->ring_buffer); + for ( i = 0; i < (1 << ved->ring_order); i++ ) + put_page_and_type(&(ved->ring_pg_struct[i])); + ved->ring_buffer = NULL; + } +} + static int vm_event_enable( struct domain *d, - struct xen_domctl_vm_event_op *vec, struct vm_event_domain **ved, + unsigned long param, + unsigned int nr_frames, int pause_flag, - int param, xen_event_channel_notification_t notification_fn) { int rc; - unsigned long ring_gfn = d->arch.hvm.params[param]; if ( !*ved ) *ved = xzalloc(struct vm_event_domain); @@ -58,26 +108,39 @@ static int vm_event_enable( /* Only one helper at a time. If the helper crashed, * the ring is in an undefined state and so is the guest. */ - if ( (*ved)->ring_page ) - return -EBUSY;; - - /* The parameter defaults to zero, and it should be - * set to something */ - if ( ring_gfn == 0 ) - return -ENOSYS; + if ( (*ved)->ring_buffer ) + return -EBUSY; vm_event_ring_lock_init(*ved); vm_event_ring_lock(*ved); rc = vm_event_init_domain(d); - if ( rc < 0 ) goto err; - rc = prepare_ring_for_helper(d, ring_gfn, &(*ved)->ring_pg_struct, - &(*ved)->ring_page); - if ( rc < 0 ) - goto err; + (*ved)->ring_order = get_order_from_pages(nr_frames); + + if ( param == XEN_VM_EVENT_ALLOC_FROM_DOMHEAP ) + { + rc = vm_event_alloc_ring(current->domain, *ved); + if ( rc < 0 ) + goto err; + } + else + { + /* param points to a specific gfn */ + unsigned long ring_gfn = d->arch.hvm.params[param]; + + /* The parameter defaults to zero, and it should be + * set to something */ + if ( ring_gfn == 0 ) + return -ENOSYS; + + rc = prepare_ring_for_helper(d, ring_gfn, &(*ved)->ring_pg_struct, + &(*ved)->ring_buffer); + if ( rc < 0 ) + goto err; + } /* Set the number of currently blocked vCPUs to 0. */ (*ved)->blocked = 0; @@ -88,12 +151,12 @@ static int vm_event_enable( if ( rc < 0 ) goto err; - (*ved)->xen_port = vec->port = rc; + (*ved)->xen_port = rc; /* Prepare ring buffer */ FRONT_RING_INIT(&(*ved)->front_ring, - (vm_event_sring_t *)(*ved)->ring_page, - PAGE_SIZE); + (vm_event_sring_t *)(*ved)->ring_buffer, + PAGE_SIZE * nr_frames); /* Save the pause flag for this particular ring. */ (*ved)->pause_flag = pause_flag; @@ -105,8 +168,8 @@ static int vm_event_enable( return 0; err: - destroy_ring_for_helper(&(*ved)->ring_page, - (*ved)->ring_pg_struct); + vm_event_destroy_ring(*ved); + vm_event_cleanup_domain(d); vm_event_ring_unlock(*ved); xfree(*ved); *ved = NULL; @@ -221,9 +284,7 @@ static int vm_event_disable(struct domain *d, struct vm_event_domain **ved) } } - destroy_ring_for_helper(&(*ved)->ring_page, - (*ved)->ring_pg_struct); - + vm_event_destroy_ring(*ved); vm_event_cleanup_domain(d); vm_event_ring_unlock(*ved); @@ -459,7 +520,7 @@ static int vm_event_grab_slot(struct vm_event_domain *ved, int foreign) { unsigned int avail_req; - if ( !ved->ring_page ) + if ( !ved->ring_buffer ) return -ENOSYS; vm_event_ring_lock(ved); @@ -498,7 +559,7 @@ static int vm_event_wait_slot(struct vm_event_domain *ved) bool_t vm_event_check_ring(struct vm_event_domain *ved) { - return (ved && ved->ring_page); + return (ved && ved->ring_buffer); } /* @@ -587,6 +648,46 @@ void vm_event_cleanup(struct domain *d) #endif } +#ifdef CONFIG_HAS_MEM_PAGING +static int vm_event_op_paging_is_supported(struct domain *d) +{ + struct p2m_domain *p2m = p2m_get_hostp2m(d); + + /* hvm fixme: p2m_is_foreign types need addressing */ + if ( is_hvm_domain(hardware_domain) ) + return -EOPNOTSUPP; + + /* Only HAP is supported */ + if ( !hap_enabled(d) ) + return -ENODEV; + + /* No paging if iommu is used */ + if ( unlikely(need_iommu(d)) ) + return -EMLINK; + + /* Disallow paging in a PoD guest */ + if ( p2m->pod.entry_count ) + return -EXDEV; + + return 0; +} +#endif /* CONFIG_HAS_MEM_PAGING */ + +#ifdef CONFIG_HAS_MEM_SHARING +static int vm_event_op_sharing_is_supported(struct domain *d) +{ + /* hvm fixme: p2m_is_foreign types need addressing */ + if ( is_hvm_domain(hardware_domain) ) + return -EOPNOTSUPP; + + /* Only HAP is supported */ + if ( !hap_enabled(d) ) + return -ENODEV; + + return 0; +} +#endif /* CONFIG_HAS_MEM_SHARING */ + int vm_event_domctl(struct domain *d, struct xen_domctl_vm_event_op *vec, XEN_GUEST_HANDLE_PARAM(void) u_domctl) { @@ -629,35 +730,19 @@ int vm_event_domctl(struct domain *d, struct xen_domctl_vm_event_op *vec, switch( vec->op ) { case XEN_VM_EVENT_ENABLE: - { - struct p2m_domain *p2m = p2m_get_hostp2m(d); - - rc = -EOPNOTSUPP; - /* hvm fixme: p2m_is_foreign types need addressing */ - if ( is_hvm_domain(hardware_domain) ) - break; - - rc = -ENODEV; - /* Only HAP is supported */ - if ( !hap_enabled(d) ) - break; - - /* No paging if iommu is used */ - rc = -EMLINK; - if ( unlikely(need_iommu(d)) ) - break; - - rc = -EXDEV; - /* Disallow paging in a PoD guest */ - if ( p2m->pod.entry_count ) + rc = vm_event_op_paging_is_supported(d); + if ( rc ) break; /* domain_pause() not required here, see XSA-99 */ - rc = vm_event_enable(d, vec, &d->vm_event_paging, _VPF_mem_paging, - HVM_PARAM_PAGING_RING_PFN, + rc = vm_event_enable(d, &d->vm_event_paging, + HVM_PARAM_PAGING_RING_PFN, 1, + _VPF_mem_paging, mem_paging_notification); - } - break; + if ( !rc ) + vec->port = d->vm_event_paging->xen_port; + + break; case XEN_VM_EVENT_DISABLE: if ( vm_event_check_ring(d->vm_event_paging) ) @@ -694,9 +779,14 @@ int vm_event_domctl(struct domain *d, struct xen_domctl_vm_event_op *vec, rc = arch_monitor_init_domain(d); if ( rc ) break; - rc = vm_event_enable(d, vec, &d->vm_event_monitor, _VPF_mem_access, - HVM_PARAM_MONITOR_RING_PFN, + + rc = vm_event_enable(d, &d->vm_event_monitor, + HVM_PARAM_MONITOR_RING_PFN, 1, + _VPF_mem_access, monitor_notification); + if ( !rc ) + vec->port = d->vm_event_monitor->xen_port; + break; case XEN_VM_EVENT_DISABLE: @@ -716,6 +806,15 @@ int vm_event_domctl(struct domain *d, struct xen_domctl_vm_event_op *vec, rc = -ENODEV; break; + case XEN_VM_EVENT_GET_PORT: + rc = -ENODEV; + if ( vm_event_check_ring(d->vm_event_monitor) ) + { + vec->port = d->vm_event_monitor->xen_port; + rc = 0; + } + break; + default: rc = -ENOSYS; break; @@ -731,20 +830,18 @@ int vm_event_domctl(struct domain *d, struct xen_domctl_vm_event_op *vec, switch( vec->op ) { case XEN_VM_EVENT_ENABLE: - rc = -EOPNOTSUPP; - /* hvm fixme: p2m_is_foreign types need addressing */ - if ( is_hvm_domain(hardware_domain) ) - break; - - rc = -ENODEV; - /* Only HAP is supported */ - if ( !hap_enabled(d) ) + rc = vm_event_op_sharing_is_supported(d); + if ( rc ) break; /* domain_pause() not required here, see XSA-99 */ - rc = vm_event_enable(d, vec, &d->vm_event_share, _VPF_mem_sharing, - HVM_PARAM_SHARING_RING_PFN, + rc = vm_event_enable(d, &d->vm_event_share, + HVM_PARAM_SHARING_RING_PFN, 1, + _VPF_mem_sharing, mem_sharing_notification); + if ( !rc ) + vec->port = d->vm_event_share->xen_port; + break; case XEN_VM_EVENT_DISABLE: @@ -778,6 +875,43 @@ int vm_event_domctl(struct domain *d, struct xen_domctl_vm_event_op *vec, return rc; } +int vm_event_get_ring_frames(struct domain *d, unsigned int id, + unsigned long frame, unsigned int nr_frames, + xen_pfn_t mfn_list[]) +{ + int rc, i; + int pause_flag; + struct vm_event_domain **ved; + xen_event_channel_notification_t notification_fn; + + switch(id) + { + case XEN_VM_EVENT_TYPE_MONITOR: + ved = &d->vm_event_monitor; + pause_flag = _VPF_mem_access; + notification_fn = monitor_notification; + + rc = arch_monitor_init_domain(d); + if ( rc ) + return rc; + break; + + default: + return -ENOSYS; + } + + rc = vm_event_enable(d, ved, XEN_VM_EVENT_ALLOC_FROM_DOMHEAP, + nr_frames, pause_flag, + notification_fn); + if ( rc ) + return rc; + + for ( i = 0; i < nr_frames; i++ ) + mfn_list[i] = mfn_x(page_to_mfn(&(*ved)->ring_pg_struct[i])); + + return 0; +} + void vm_event_vcpu_pause(struct vcpu *v) { ASSERT(v == current); diff --git a/xen/include/public/domctl.h b/xen/include/public/domctl.h index ac4ced2..066d4da 100644 --- a/xen/include/public/domctl.h +++ b/xen/include/public/domctl.h @@ -772,6 +772,7 @@ struct xen_domctl_gdbsx_domstatus { #define XEN_VM_EVENT_ENABLE 0 #define XEN_VM_EVENT_DISABLE 1 #define XEN_VM_EVENT_RESUME 2 +#define XEN_VM_EVENT_GET_PORT 3 /* * Domain memory paging diff --git a/xen/include/public/memory.h b/xen/include/public/memory.h index 8fc27ce..09400f5 100644 --- a/xen/include/public/memory.h +++ b/xen/include/public/memory.h @@ -612,6 +612,7 @@ struct xen_mem_acquire_resource { #define XENMEM_resource_ioreq_server 0 #define XENMEM_resource_grant_table 1 +#define XENMEM_resource_vm_event 2 /* * IN - a type-specific resource identifier, which must be zero diff --git a/xen/include/xen/sched.h b/xen/include/xen/sched.h index 0ba80cb..be01f93 100644 --- a/xen/include/xen/sched.h +++ b/xen/include/xen/sched.h @@ -286,9 +286,10 @@ struct vm_event_domain /* The ring has 64 entries */ unsigned char foreign_producers; unsigned char target_producers; - /* shared ring page */ - void *ring_page; + /* shared ring pages */ + void *ring_buffer; struct page_info *ring_pg_struct; + unsigned int ring_order; /* front-end ring */ vm_event_front_ring_t front_ring; /* event channel port (vcpu0 only) */ diff --git a/xen/include/xen/vm_event.h b/xen/include/xen/vm_event.h index 2ff6e1c..d9c5e93 100644 --- a/xen/include/xen/vm_event.h +++ b/xen/include/xen/vm_event.h @@ -80,6 +80,10 @@ void vm_event_set_registers(struct vcpu *v, vm_event_response_t *rsp); void vm_event_monitor_next_interrupt(struct vcpu *v); +int vm_event_get_ring_frames(struct domain *d, unsigned int id, + unsigned long frame, unsigned int nr_frames, + xen_pfn_t mfn_list[]); + #endif /* __VM_EVENT_H__ */ /* -- 2.7.4 _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxxxxxxxxx https://lists.xenproject.org/mailman/listinfo/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |