|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [Xen-devel] [PATCH RFC v2 8/9] xen/arm: Implement hypercall for dirty page tracing (shadow op)
On Wed, 3 Jul 2013, Jaeyong Yoo wrote:
> From: Elena Pyatunina <e.pyatunina@xxxxxxxxxxx>
>
> Add hypercall (shadow op: enable/disable and clean/peek dirted page bitmap)
>
> Signed-off-by: Elena Pyatunina <e.pyatunina@xxxxxxxxxxx>
> Singed-off-by: Alexey Sokolov <sokolov.a@xxxxxxxxxxx>
> ---
> xen/arch/arm/domain.c | 4 +
> xen/arch/arm/domctl.c | 19 ++++
> xen/arch/arm/p2m.c | 219
> +++++++++++++++++++++++++++++++++++++++++++++-
> xen/include/asm-arm/p2m.h | 2 +
> 4 files changed, 242 insertions(+), 2 deletions(-)
>
> diff --git a/xen/arch/arm/domain.c b/xen/arch/arm/domain.c
> index 73dd0de..e3fc533 100644
> --- a/xen/arch/arm/domain.c
> +++ b/xen/arch/arm/domain.c
> @@ -484,6 +484,10 @@ int arch_domain_create(struct domain *d, unsigned int
> domcr_flags)
> d->arch.vpidr = boot_cpu_data.midr.bits;
> d->arch.vmpidr = boot_cpu_data.mpidr.bits;
>
> + /* init for dirty-page tracing */
> + d->arch.dirty.count = 0;
> + d->arch.dirty.top = NULL;
> +
> clear_page(d->shared_info);
> share_xen_page_with_guest(
> virt_to_page(d->shared_info), d, XENSHARE_writable);
> diff --git a/xen/arch/arm/domctl.c b/xen/arch/arm/domctl.c
> index a0719b0..bc06534 100644
> --- a/xen/arch/arm/domctl.c
> +++ b/xen/arch/arm/domctl.c
> @@ -67,6 +67,14 @@ long arch_do_domctl(struct xen_domctl *domctl, struct
> domain *d,
> goto gethvmcontext_out;
> }
>
> + /* Allocate our own marshalling buffer */
> + ret = -ENOMEM;
> + if ( (c.data = xmalloc_bytes(c.size)) == NULL )
> + {
> + printk("(gethvmcontext) xmalloc_bytes failed: %d\n", c.size );
> + goto gethvmcontext_out;
> + }
> +
> domain_pause(d);
> ret = hvm_save(d, &c);
> domain_unpause(d);
Shouldn't this hunk belong to patch #2?
> @@ -85,6 +93,17 @@ long arch_do_domctl(struct xen_domctl *domctl, struct
> domain *d,
> xfree(c.data);
> }
> break;
> + case XEN_DOMCTL_shadow_op:
> + {
> + ret = dirty_mode_op(d, &domctl->u.shadow_op);
> + if ( (&domctl->u.shadow_op)->op == XEN_DOMCTL_SHADOW_OP_CLEAN
> + || (&domctl->u.shadow_op)->op == XEN_DOMCTL_SHADOW_OP_PEEK)
> + {
> + copyback = 1;
> + }
> + }
> + break;
> +
> default:
> return -EINVAL;
> }
> diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c
> index bae7af7..fb8ce10 100644
> --- a/xen/arch/arm/p2m.c
> +++ b/xen/arch/arm/p2m.c
> @@ -69,8 +69,8 @@ out:
>
> spin_unlock(&p2m->lock);
>
> - return ( ((xen_pfn_t)first_index << FIRST_SHIFT) |
> - (second_index << SECOND_SHIFT) |
> + return ( ((xen_pfn_t)first_index << FIRST_SHIFT) |
> + (second_index << SECOND_SHIFT) |
> (third_index << THIRD_SHIFT)
> ) >> PAGE_SHIFT;
Does this belong here? If so, why?
> @@ -496,6 +496,221 @@ void mark_dirty(struct domain *d, paddr_t addr)
> spin_unlock(&d->arch.dirty.lock);
> }
>
> +static void free_dirty_bitmap(struct domain *d)
> +{
> + struct page_info *pg;
> +
> + spin_lock(&d->arch.dirty.lock);
> +
> + if(d->arch.dirty.top)
> + {
> + while ( (pg = page_list_remove_head(&d->arch.dirty.pages)) )
> + free_domheap_page(pg);
> + }
> + d->arch.dirty.top = NULL;
> + d->arch.dirty.count = 0;
> +
> + spin_unlock(&d->arch.dirty.lock);
> +}
> +
> +/* Change types across all p2m entries in a domain */
> +static void p2m_change_entry_type_global(struct domain *d, enum mg ot, enum
> mg nt)
> +{
> + struct p2m_domain *p2m = &d->arch.p2m;
> + int i1, i2, i3;
> + int first_index = first_table_offset(GUEST_RAM_BASE);
> + int second_index = second_table_offset(GUEST_RAM_BASE);
> + int third_index = third_table_offset(GUEST_RAM_BASE);
you shouldn't assume GUEST_RAM_BASE is static anywhere
> + lpae_t *first = __map_domain_page(p2m->first_level);
> + lpae_t pte, *second = NULL, *third = NULL;
> +
> + BUG_ON(!first && "Can't map first level p2m.");
> +
> + spin_lock(&p2m->lock);
> +
> + for (i1 = first_index; i1 < LPAE_ENTRIES*2; ++i1)
> + {
> + lpae_walk_t first_pte = first[i1].walk;
> + if (!first_pte.valid || !first_pte.table)
> + goto out;
> +
> + second = map_domain_page(first_pte.base);
> + BUG_ON(!second && "Can't map second level p2m.");
> + for(i2 = second_index; i2 < LPAE_ENTRIES; ++i2)
> + {
> + lpae_walk_t second_pte = second[i2].walk;
> + if (!second_pte.valid || !second_pte.table)
> + goto out;
> +
> + third = map_domain_page(second_pte.base);
> + BUG_ON(!third && "Can't map third level p2m.");
> +
> + for (i3 = third_index; i3 < LPAE_ENTRIES; ++i3)
> + {
> + lpae_walk_t third_pte = third[i3].walk;
> + int write = 0;
> + if (!third_pte.valid)
> + goto out;
> +
> + pte = third[i3];
> + if (pte.p2m.write == 1 && nt == mg_ro)
> + {
> + pte.p2m.write = 0;
> + write = 1;
> + }
> + else if (pte.p2m.write == 0 && nt == mg_rw)
> + {
> + pte.p2m.write = 1;
> + write = 1;
> + }
> + if (write)
> + write_pte(&third[i3], pte);
> + }
> + unmap_domain_page(third);
> +
> + third = NULL;
> + third_index = 0;
> + }
> + unmap_domain_page(second);
> +
> + second = NULL;
> + second_index = 0;
> + third_index = 0;
> + }
> +
> +out:
> + flush_tlb_all_local();
> + if (third) unmap_domain_page(third);
> + if (second) unmap_domain_page(second);
> + if (first) unmap_domain_page(first);
> +
> + spin_unlock(&p2m->lock);
> +}
> +
> +/* Read a domain's log-dirty bitmap and stats.
> + * If the operation is a CLEAN, clear the bitmap and stats. */
> +static int log_dirty_op(struct domain *d, xen_domctl_shadow_op_t *sc)
> +{
> + int i1, i2, rv = 0, clean = 0, peek = 1;
> + xen_pfn_t *first = NULL, *second = NULL;
> + unsigned long pages = 0, *third = NULL;
> +
> + clean = (sc->op == XEN_DOMCTL_SHADOW_OP_CLEAN);
> +
> + if (guest_handle_is_null(sc->dirty_bitmap))
> + peek = 0;
> +
> + domain_pause(d);
> +
> + printk("DEBUG: log-dirty %s: dom %u dirty=%u\n",
> + (clean) ? "clean" : "peek",
> + d->domain_id, d->arch.dirty.count);
> +
> + sc->stats.dirty_count = d->arch.dirty.count;
> +
> + spin_lock(&d->arch.dirty.lock);
> + first = d->arch.dirty.top ? __map_domain_page(d->arch.dirty.top) : NULL;
> +
> + /* 32-bit address space starts from 0 and ends at ffffffff.
This should be a 40 bit address space due to LPAE support
> + * First level of p2m tables is indexed by bits 39-30.
> + * So significant bits for 32-bit addresses in first table are 31-30.
> + * So we need to iterate from 0 to 4.
> + * But RAM starts from 2 gigabytes. So start from 2 instead.
> + * TODO remove this hardcode
I wouldn't assume that the guest ram start at 2GB
> + */
> + for (i1 = 2; (pages < sc->pages) && (i1 < 4); ++i1)
> + {
> + second = (first && first[i1]) ? map_domain_page(first[i1]) : NULL;
> +
> + for (i2 = 0; (i2 < LPAE_ENTRIES); ++i2)
> + {
> + unsigned int bytes = LPAE_ENTRIES/8;
> + third = (second && second[i2]) ? map_domain_page(second[i2]) :
> NULL;
> + if ( unlikely(((sc->pages - pages + 7) >> 3) < bytes) )
> + bytes = (unsigned int)((sc->pages - pages + 7) >> 3);
> + if (peek)
^ code style
> + {
> + if ( (third ? copy_to_guest_offset(sc->dirty_bitmap, pages
> >> 3,
> + (uint8_t *)third, bytes)
> + : clear_guest_offset(sc->dirty_bitmap, pages >>
> 3,
> + (uint8_t*)NULL /*only type
> matters*/,
> + bytes)) != 0 )
> + {
> + rv = 1;
> + goto out;
> + }
> + }
> + pages += bytes << 3;
> + if (third)
^ code style
> + {
> + if (clean) clear_page(third);
> + unmap_domain_page(third);
> + }
> + }
> + if (second) unmap_domain_page(second);
> + }
> + if (first) unmap_domain_page(first);
> +
> + spin_unlock(&d->arch.dirty.lock);
> +
> + if ( pages < sc->pages )
> + sc->pages = pages;
> +
> + if (clean)
^ code style
> + {
> + p2m_change_entry_type_global(d, mg_rw, mg_ro);
> + free_dirty_bitmap(d);
> + }
> +
> +out:
> + if (rv)
> + {
> + spin_unlock(&d->arch.dirty.lock);
> + }
> + domain_unpause(d);
> + return rv;
> +}
> +
> +long dirty_mode_op(struct domain *d, xen_domctl_shadow_op_t *sc)
> +{
> + long ret = 0;
> +
> + switch (sc->op)
> + {
> + case XEN_DOMCTL_SHADOW_OP_ENABLE_LOGDIRTY:
> + case XEN_DOMCTL_SHADOW_OP_OFF:
> + {
> + enum mg ot = mg_clear, nt = mg_clear;
> +
> + ot = sc->op == XEN_DOMCTL_SHADOW_OP_ENABLE_LOGDIRTY ? mg_rw :
> mg_ro;
> + nt = sc->op == XEN_DOMCTL_SHADOW_OP_OFF ? mg_rw : mg_ro;
> +
> + domain_pause(d);
> +
> + d->arch.dirty.mode = sc->op == XEN_DOMCTL_SHADOW_OP_OFF ? 0 : 1;
> + p2m_change_entry_type_global(d, ot, nt);
> +
> + if (sc->op == XEN_DOMCTL_SHADOW_OP_OFF)
> + free_dirty_bitmap(d);
> +
> + domain_unpause(d);
Do we need a lock to make sure that all the operations happen
atomically?
> + }
> + break;
> +
> + case XEN_DOMCTL_SHADOW_OP_CLEAN:
> + case XEN_DOMCTL_SHADOW_OP_PEEK:
> + {
> + ret = log_dirty_op(d, sc);
> + }
> + break;
For symmetry maybe it would be better to pause the domain outside
log_dirty_op
> + default:
> + return -ENOSYS;
> + }
> + return ret;
> +}
> +
> /*
> * Local variables:
> * mode: C
> diff --git a/xen/include/asm-arm/p2m.h b/xen/include/asm-arm/p2m.h
> index fd5890f..498cf0b 100644
> --- a/xen/include/asm-arm/p2m.h
> +++ b/xen/include/asm-arm/p2m.h
> @@ -2,6 +2,7 @@
> #define _XEN_P2M_H
>
> #include <xen/mm.h>
> +#include <public/domctl.h>
>
> struct domain;
>
> @@ -112,6 +113,7 @@ static inline int get_page_and_type(struct page_info
> *page,
>
> enum mg { mg_clear, mg_ro, mg_rw, mg_rx };
> void mark_dirty(struct domain *d, paddr_t addr);
> +long dirty_mode_op(struct domain *d, xen_domctl_shadow_op_t *sc);
>
> #endif /* _XEN_P2M_H */
>
> --
> 1.8.1.2
>
>
> _______________________________________________
> Xen-devel mailing list
> Xen-devel@xxxxxxxxxxxxx
> http://lists.xen.org/xen-devel
>
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |