Add libxc API that sets mem_access type for an array of gfns
PATCH 1/1 Add libxc API that sets mem_access type for an array of gfns
When the mem_access type needs to be changed for multiple discontiguous gfns, calling xc_hvm_set_mem_access() multiple times does not perform very well. The main pain points are the multiple libxc calls themselves plus the multiple map_domain_page() / unmap_domain_page() and ept_sync_domain() calls for each ept_set_entry(gfn). The following patches adds a new mem_access API that sets the mem_access type for an array of guest physical addresses in one libxc call with minimal map_domain_page() / unmap_domain_page() and ept_sync_domain() calls.
PATCH 1/1 x86/mm: Split ept_set_entry()
xen/arch/x86/mm/p2m-ept.c | 60 ++++++++++++++++++++++++++++------------------ 1 files changed, 37 insertions(+), 23 deletions(-)
Split ept_set_entry() such that mapping and unmapping of the page tables and ept_sync occurrs outside of it.
xen/arch/x86/mm/p2m-ept.c +37 -23
@@ -272,14 +272,15 @@ static int ept_next_level(struct p2m_dom }/* -* ept_set_entry() computes 'need_modify_vtd_table' for itself, +* __ept_set_entry() computes 'need_modify_vtd_table' for itself, * by observing whether any gfn->mfn translations are modified. */ static int -ept_set_entry (struct p2m_domain *p2m,unsigned long gfn,mfn_t mfn, -unsigned int order,p2m_type_t p2mt,p2m_access_t p2ma) +__ept_set_entry (struct p2m_domain *p2m,unsigned long gfn,mfn_t mfn, +unsigned int order,p2m_type_t p2mt,p2m_access_t p2ma, +ept_entry_t *table,ept_entry_t *old_entry,bool_t *needs_sync) { -ept_entry_t *table, *ept_entry =NULL ; +ept_entry_t *ept_entry =NULL ;unsigned long gfn_remainder = gfn;unsigned long offset =0 ;u32 index;
@@ -288,11 +289,9 @@ ept_set_entry(struct p2m_domain *p2m, unint ret =0 ;bool_t direct_mmio = (p2mt == p2m_mmio_direct);uint8_t ipat =0 ; -int need_modify_vtd_table =1 ; -int vtd_pte_present =0 ; -int needs_sync =1 ; +bool_t need_modify_vtd_table =1 ; +bool_t vtd_pte_present =0 ;struct domain *d = p2m->domain; -ept_entry_t old_entry = { .epte =0 };/* * the caller must make sure:
@@ -305,12 +304,6 @@ ept_set_entry(struct p2m_domain *p2m, un (order % EPT_TABLE_ORDER) )return 0 ; -ASSERT ((target ==2 &&hvm_hap_has_1gb (d)) || - (target ==1 &&hvm_hap_has_2mb (d)) || - (target ==0 )); - - table =map_domain_page (ept_get_asr (d)); -for ( i =ept_get_wl (d); i > target; i-- ) { ret =ept_next_level (p2m,0 , &table, &gfn_remainder, i);
@@ -327,7 +320,7 @@ ept_set_entry(struct p2m_domain *p2m, un ept_entry = table + index; -/* In case VT-d uses same page table, this flag is needed by VT-d */ +/* In case VT-d uses same page table, this flag is needed by VT-d */ vtd_pte_present =is_epte_present (ept_entry) ?1 :0 ;/*
@@ -352,7 +345,7 @@ ept_set_entry(struct p2m_domain *p2m, un* the intermediate tables will be freed below after the ept flush * * Read-then-write is OK because we hold the p2m lock. */ - old_entry = *ept_entry; + old_entry->epte = ept_entry->epte;if (mfn_valid (mfn_x (mfn)) || direct_mmio ||p2m_is_paged (p2mt) || (p2mt == p2m_ram_paging_in) )
@@ -369,7 +362,7 @@ ept_set_entry(struct p2m_domain *p2m, un new_entry.mfn =mfn_x (mfn); -if ( old_entry.mfn == new_entry.mfn ) +if ( old_entry->mfn == new_entry.mfn ) need_modify_vtd_table =0 ;ept_p2m_type_to_flags (&new_entry, p2mt, p2ma);
@@ -435,12 +428,7 @@ ept_set_entry(struct p2m_domain *p2m, un/* Success */ rv =1 ; -out: -unmap_domain_page (table); - -if ( needs_sync ) -ept_sync_domain (p2m->domain); - + out:if ( rv && iommu_enabled &&need_iommu (p2m->domain) && need_modify_vtd_table ) {if ( iommu_hap_pt_share )
@@ -473,6 +461,32 @@ out: } } +return rv; +} + +static int +ept_set_entry (struct p2m_domain *p2m,unsigned long gfn,mfn_t mfn, +unsigned int order,p2m_type_t p2mt,p2m_access_t p2ma) +{ +ept_entry_t *table; +int target = order /EPT_TABLE_ORDER ; +int rv =0 ; +bool_t needs_sync =1 ; +struct domain *d = p2m->domain; +ept_entry_t old_entry = { .epte =0 }; + +ASSERT ((target ==2 &&hvm_hap_has_1gb (d)) || + (target ==1 &&hvm_hap_has_2mb (d)) || + (target ==0 )); + + table =map_domain_page (ept_get_asr (d)); + rv =__ept_set_entry (p2m, gfn, mfn, order, p2mt, p2ma, table, &old_entry, + &needs_sync); +unmap_domain_page (table); + +if ( needs_sync ) +ept_sync_domain (p2m->domain); +/* Release the old intermediate tables, if any. This has to be the last thing we do, after the ept_sync_domain() and removal from the iommu tables, so as to avoid a potential
PATCH 1/1 mem_access: Add xc_hvm_mem_access_bulk() API
In reply to Aravindh Puthiyaparambiltools/libxc/xc_misc.c | 51 ++++++++++++++++++++++++++++++++++ tools/libxc/xenctrl.h | 11 +++++++ xen/arch/x86/hvm/hvm.c | 45 ++++++++++++++++++++++++++++++ xen/arch/x86/mm/p2m-ept.c | 61 +++++++++++++++++++++++++++++++++++++++++ xen/arch/x86/mm/p2m-pt.c | 1 + xen/arch/x86/mm/p2m.c | 36 ++++++++++++++++++++++++ xen/include/asm-x86/p2m.h | 18 +++++++++++- xen/include/public/hvm/hvm_op.h | 18 ++++++++++++ 8 files changed, 240 insertions(+), 1 deletions(-)
int xc_hvm_set_mem_access_bulk(
xc_interface *xch, domid_t dom, hvmmem_access_t memaccess, xen_pfn_t *arr, int *err, uint64_t num);
Set the array of guest physical addresses to a specific mem_access type. Allowed types are HVMMEM_access_default, HVMMEM_access_n, any combination of HVM_access_ + (rwx), and HVM_access_rx2rw.
When a gfn cannot be set to the specified access, its respective field in @err is set to the corresponding errno value.
tools/libxc/xc_misc.c +51 -0
@@ -570,6 +570,57 @@ int xc_hvm_set_mem_access(return rc; } +int xc_hvm_set_mem_access_bulk ( +xc_interface *xch,domid_t dom,hvmmem_access_t mem_access, +xen_pfn_t *arr,int *err,uint64_t nr) +{ +DECLARE_HYPERCALL ; + DECLARE_HYPERCALL_BUFFER(struct xen_hvm_set_mem_access_bulk, arg); +DECLARE_HYPERCALL_BOUNCE (arr,sizeof (xen_pfn_t) * nr, +XC_HYPERCALL_BUFFER_BOUNCE_IN ); +DECLARE_HYPERCALL_BOUNCE (err,sizeof (int ) * nr, +XC_HYPERCALL_BUFFER_BOUNCE_OUT ); +int rc; + + arg =xc_hypercall_buffer_alloc (xch, arg,sizeof (*arg)); +if ( arg ==NULL ) + { +PERROR ("Could not allocate memory for xc_hvm_set_mem_access_bulk hypercall" ); +return -1 ; + } + +if (xc_hypercall_bounce_pre (xch, arr) ) { +PERROR ("Could not bounce arr for xc_hvm_set_mem_access_bulk hypercall" ); + rc =-1 ; + goto out; + } + +if (xc_hypercall_bounce_pre (xch, err) ) { +PERROR ("Could not bounce err for xc_hvm_set_mem_access_bulk hypercall" ); + rc =-1 ; + goto out; + } + + arg->domid = dom; + arg->hvmmem_access = mem_access; + arg->nr = nr; +set_xen_guest_handle (arg->arr, arr); +set_xen_guest_handle (arg->err, err); + + hypercall.op = __HYPERVISOR_hvm_op; + hypercall.arg[0 ] = HVMOP_set_mem_access_bulk; + hypercall.arg[1 ] =HYPERCALL_BUFFER_AS_ARG (arg); + + rc =do_xen_hypercall (xch, &hypercall); + +out: +xc_hypercall_buffer_free (xch, arg); +xc_hypercall_bounce_post (xch, arr); +xc_hypercall_bounce_post (xch, err); + +return rc; +} +int xc_hvm_get_mem_access (xc_interface *xch,domid_t dom,uint64_t pfn,hvmmem_access_t * mem_access) {
tools/libxc/xenctrl.h +11 -0
@@ -1568,6 +1568,17 @@ int xc_hvm_set_mem_access( xc_interface *xch, domid_t dom, hvmmem_access_t memaccess, uint64_t first_pfn, uint64_t nr);/* +* Set the arry of pfns to a specific access. +* When a pfn cannot be set to the specified access, its respective field in +* @err is set to the corresponding errno value. +* Allowed types are HVMMEM_access_default, HVMMEM_access_n, any combination of +* HVM_access_ + (rwx), and HVM_access_rx2rw +*/ +int xc_hvm_set_mem_access_bulk ( +xc_interface *xch,domid_t dom,hvmmem_access_t memaccess, +xen_pfn_t *arr,int *err,uint64_t num); + +/* * Gets the mem access for the given page (returned in memacess on success) */ int xc_hvm_get_mem_access(
xen/arch/x86/hvm/hvm.c +45 -0
51 lines not on this page · raw message
xen/arch/x86/mm/p2m-ept.c +61 -0
73 lines not on this page · raw message
xen/arch/x86/mm/p2m-pt.c +1 -0
@@ -1139,6 +1139,7 @@ long p2m_pt_audit_p2m(struct p2m_domainvoid p2m_pt_init (struct p2m_domain *p2m) { p2m->set_entry = p2m_set_entry; + p2m->set_entry_bulk =NULL ; p2m->get_entry = p2m_gfn_to_mfn; p2m->change_entry_type_global = p2m_change_type_global; p2m->write_p2m_entry = paging_write_p2m_entry;
xen/arch/x86/mm/p2m.c +36 -0
@@ -1334,6 +1334,42 @@ int p2m_set_mem_access(struct domain *d,return rc; } +int p2m_set_access_bulk (struct domain *d,xen_pfn_t *arr,int *err, +uint64_t nr,hvmmem_access_t access) +{ +struct p2m_domain *p2m =p2m_get_hostp2m (d); +p2m_access_t a; +p2m_access_t memaccess[] = { + p2m_access_n, + p2m_access_r, + p2m_access_w, + p2m_access_rw, + p2m_access_x, + p2m_access_rx, + p2m_access_wx, + p2m_access_rwx, + p2m_access_rx2rw, + p2m_access_n2rwx, + p2m->default_access, + }; +int rc =0 ; + +ASSERT (p2m->set_entry_bulk); + +if ( (unsigned ) access >= HVMMEM_access_default ) +return -EINVAL ; + + a = memaccess[access]; + +p2m_lock (p2m); +if ( p2m->set_entry_bulk (p2m, arr, err, nr,PAGE_ORDER_4K , a) ==0 ) + rc = -ENOMEM ; +p2m_unlock (p2m); + +return rc; +} + +/* Get access type for a pfn * If pfn == -1ul, gets the default access type */ int p2m_get_mem_access(struct domain *d, unsigned long pfn,
xen/include/asm-x86/p2m.h +17 -1
@@ -232,6 +232,13 @@ struct p2m_domain { mfn_t mfn, unsigned int page_order, p2m_type_t p2mt, p2m_access_t p2ma); +int (*set_entry_bulk)(struct p2m_domain *p2m, +xen_pfn_t *arr, +int *err, +uint64_t nr, +unsigned int order, +p2m_access_t p2ma); + mfn_t (*get_entry )(struct p2m_domain *p2m, unsigned long gfn, p2m_type_t *p2mt,
@@ -568,6 +575,11 @@ void p2m_mem_access_resume(struct domainint p2m_set_mem_access (struct domain *d,unsigned long start_pfn,uint32_t nr,hvmmem_access_t access); +/* Set access type for an array of pfns. set_mem_access success or failure is +* returned in the err array. */ +int p2m_set_access_bulk (struct domain *d,xen_pfn_t *arr,int *err, +uint64_t nr,hvmmem_access_t access); +/* Get access type for a pfn * If pfn == -1ul, gets the default access type */ int p2m_get_mem_access(struct domain *d, unsigned long pfn,
@@ -583,7 +595,11 @@ static inline int p2m_set_mem_access(strunsigned long start_pfn, uint32_t nr, hvmmem_access_t access) { return -EINVAL; } -static inline int p2m_get_mem_access (struct domain *d,unsigned long pfn, +static inline int p2m_set_access_bulk (struct domain *d,xen_pfn_t *arr, +int *err,uint64_t nr, +hvmmem_access_t access) +{return -EINVAL ; } +static inline int p2m_get_mem_access (struct domain *d,unsigned long pfn,hvmmem_access_t *access) {return -EINVAL ; }#endif
xen/include/public/hvm/hvm_op.h +18 -0
@@ -261,4 +261,22 @@ DEFINE_XEN_GUEST_HANDLE(xen_hvm_inject_m#endif /* defined(__XEN__) || defined(__XEN_TOOLS__) */ +#if defined(__XEN__) || defined(__XEN_TOOLS__) +#define HVMOP_set_mem_access_bulk 17 +/* Notify that a array of pfns is to have specific access types */ +struct xen_hvm_set_mem_access_bulk { +/* Domain to be updated. */ +domid_t domid; +/* Memory type */ +uint16_t hvmmem_access;/* hvm_access_t */ +/* Array of pfns */ +XEN_GUEST_HANDLE_64 (xen_pfn_t) arr; +XEN_GUEST_HANDLE_64 (int ) err ; +/* Number of entries */ +uint64_t nr; +}; +typedef struct xen_hvm_set_mem_access_bulk xen_hvm_set_mem_access_bulk_t ; +DEFINE_XEN_GUEST_HANDLE (xen_hvm_set_mem_access_bulk_t); +#endif +#endif /* __XEN_PUBLIC_HVM_HVM_OP_H__ */
Quoted 1 line · Aravindh Puthiyaparambil
When the
mem_accesstype needs to be changed for multiple discontiguous gfns, callingxc_hvm_set_mem_access()multiple times does not perform very well. The main pain points are the multiple libxc calls themselves plus the multiplemap_domain_page()/unmap_domain_page()andept_sync_domain()calls for eachept_set_entry(gfn). The following patches adds a newmem_accessAPI that sets themem_accesstype for an array of guest physical addresses in one libxc call with minimalmap_domain_page()/unmap_domain_page()andept_sync_domain()calls.
Are you sure that your bulk code actually works? It seems to me that your __ept_set_entry function assumes that table still points to the top of the p2m. The "for ( i = ept_get_wl(d); i > target; i-- )" loop will walk the table, and so in the subsequent calls from your bulk loop, this won't work?
I think you need something like an iterator, the context of which can be passed around...
Also, the call to ept_get_entry in your bulk loop will do a walk in every iteration, it seems a bit arbitrary to only (try to) avoid one and not the other. But I guess the "win" is from reducing the number of ept_sync_domain calls.
christian
Quoted 5 lines · Christian Limpach
Quoted 1 line · Aravindh Puthiyaparambil
When the
mem_accesstype needs to be changed for multiple discontiguous gfns, callingxc_hvm_set_mem_access()multiple times does not perform very well. The main pain points are the multiple libxc calls themselves plus the multiplemap_domain_page()/unmap_domain_page()andept_sync_domain()calls for eachept_set_entry(gfn). The following patches adds a newmem_accessAPI that sets themem_accesstype for an array of guest physical addresses in one libxc call with minimalmap_domain_page()/unmap_domain_page()andept_sync_domain()calls.
Quoted 8 lines
Are you sure that your bulk code actually works? It seems to me that your
__ept_set_entryfunction assumes that table still points to the top of the p2m. The "for ( i =ept_get_wl(d); i > target; i-- )" loop will walk the table, and so in the subsequent calls from your bulk loop, this won't work?I think you need something like an iterator, the context of which can be passed around...
Duh! You are right. My test code has been giving me a false positive. I completely misread how ept_next_level() works.
Quoted 4 lines
Also, the call to
ept_get_entryin your bulk loop will do a walk in every iteration, it seems a bit arbitrary to only (try to) avoid one and not the other. But I guess the "win" is from reducing the number ofept_sync_domaincalls.
You are right. I was mainly focusing on removing the multiple libxc calls and reducing the ept_sync_domain calls. I thought removing the map and unmap of the p2m top page was an extra optimization which I obviously messed up. I will rework the patch to only stick with the original optimization I had in mind.
Thanks,
Aravindh
Quoted 24 lines · Aravindh Puthiyaparambil
Quoted 5 lines · Christian Limpach
Quoted 1 line · Aravindh Puthiyaparambil
When the
mem_accesstype needs to be changed for multiple discontiguous gfns, callingxc_hvm_set_mem_access()multiple times does not perform very well. The main pain points are the multiple libxc calls themselves plus the multiplemap_domain_page()/unmap_domain_page()andept_sync_domain()calls for eachept_set_entry(gfn). The following patches adds a newmem_accessAPI that sets themem_accesstype for an array of guest physical addresses in one libxc call with minimalmap_domain_page()/unmap_domain_page()andept_sync_domain()calls.Quoted 8 lines
Are you sure that your bulk code actually works? It seems to me that your
__ept_set_entryfunction assumes that table still points to the top of the p2m. The "for ( i =ept_get_wl(d); i > target; i-- )" loop will walk the table, and so in the subsequent calls from your bulk loop, this won't work?I think you need something like an iterator, the context of which can be passed around...
Duh! You are right. My test code has been giving me a false positive. I completely misread how
ept_next_level()works.Quoted 4 lines
Also, the call to
ept_get_entryin your bulk loop will do a walk in every iteration, it seems a bit arbitrary to only (try to) avoid one and not the other. But I guess the "win" is from reducing the number ofept_sync_domaincalls.
Quoted 5 lines
You are right. I was mainly focusing on removing the multiple libxc calls and reducing the
ept_sync_domaincalls. I thought removing the map and unmap of the p2m top page was an extra optimization which I obviously messed up. I will rework the patch to only stick with the original optimization I had in mind.
Does this look correct now?
Thanks,
Aravindh
changeset: 25252:b1bde8691257 summary: x86/mm: Split ept_set_entry()
xen/arch/x86/mm/p2m-ept.c +8 -16
@@ -272,12 +272,13 @@ static int ept_next_level(struct p2m_dom }/* -* ept_set_entry() computes 'need_modify_vtd_table' for itself, +* __ept_set_entry() computes 'need_modify_vtd_table' for itself, * by observing whether any gfn->mfn translations are modified. */ static int -ept_set_entry (struct p2m_domain *p2m,unsigned long gfn,mfn_t mfn, -unsigned int order,p2m_type_t p2mt,p2m_access_t p2ma) +__ept_set_entry (struct p2m_domain *p2m,unsigned long gfn,mfn_t mfn, +unsigned int order,p2m_type_t p2mt,p2m_access_t p2ma, +ept_entry_t *old_entry,bool_t *needs_sync) {ept_entry_t *table, *ept_entry =NULL ;unsigned long gfn_remainder = gfn;
@@ -288,11 +289,9 @@ ept_set_entry(struct p2m_domain *p2m, unint ret =0 ;bool_t direct_mmio = (p2mt == p2m_mmio_direct);uint8_t ipat =0 ; -int need_modify_vtd_table =1 ; -int vtd_pte_present =0 ; -int needs_sync =1 ; +bool_t need_modify_vtd_table =1 ; +bool_t vtd_pte_present =0 ;struct domain *d = p2m->domain; -ept_entry_t old_entry = { .epte =0 };/* * the caller must make sure:
@@ -305,10 +304,6 @@ ept_set_entry(struct p2m_domain *p2m, un (order % EPT_TABLE_ORDER) )return 0 ; -ASSERT ((target ==2 &&hvm_hap_has_1gb (d)) || - (target ==1 &&hvm_hap_has_2mb (d)) || - (target ==0 )); - table =map_domain_page (ept_get_asr (d));for ( i =ept_get_wl (d); i > target; i-- )
@@ -352,7 +347,7 @@ ept_set_entry(struct p2m_domain *p2m, un* the intermediate tables will be freed below after the ept flush * * Read-then-write is OK because we hold the p2m lock. */ - old_entry = *ept_entry; + old_entry->epte = ept_entry->epte;if (mfn_valid (mfn_x (mfn)) || direct_mmio ||p2m_is_paged (p2mt) || (p2mt == p2m_ram_paging_in) )
@@ -369,7 +364,7 @@ ept_set_entry(struct p2m_domain *p2m, un new_entry.mfn =mfn_x (mfn); -if ( old_entry.mfn == new_entry.mfn ) +if ( old_entry->mfn == new_entry.mfn ) need_modify_vtd_table =0 ;ept_p2m_type_to_flags (&new_entry, p2mt, p2ma);
@@ -438,9 +433,6 @@ ept_set_entry(struct p2m_domain *p2m, un out:unmap_domain_page (table); -if ( needs_sync ) -ept_sync_domain (p2m->domain); - if ( rv && iommu_enabled && need_iommu(p2m->domain) &&
need_modify_vtd_table )
{
if ( iommu_hap_pt_share )
@@ -473,6 +465,28 @@ out:
}
}+ return rv;
+}
+
+static int
+ept_set_entry(struct p2m_domain *p2m, unsigned long gfn, mfn_t mfn,
+ unsigned int order, p2m_type_t p2mt, p2m_access_t p2ma)
+{
+ int target = order / EPT_TABLE_ORDER;
+ int rv = 0;
+ bool_t needs_sync = 1;
+ ept_entry_t old_entry = { .epte = 0 };
+
+ ASSERT((target == 2 && hvm_hap_has_1gb(d)) ||
+ (target == 1 && hvm_hap_has_2mb(d)) ||+ (target == 0));
+
+ rv = __ept_set_entry(p2m, gfn, mfn, order, p2mt, p2ma, &old_entry,
+ &needs_sync);
+
+ if ( needs_sync )
+ ept_sync_domain(p2m->domain);
+
/* Release the old intermediate tables, if any. This has to be the
last thing we do, after the ept_sync_domain() and removal from the iommu tables, so as to avoid a potential
changeset: 25253:07225bc67197 summary: mem_access: Add xc_hvm_mem_access_bulk() API
tools/libxc/xc_misc.c +16 -0
@@ -570,6 +570,57 @@ int xc_hvm_set_mem_access(return rc; } +int xc_hvm_set_mem_access_bulk ( +xc_interface *xch,domid_t dom,hvmmem_access_t mem_access, +xen_pfn_t *arr,int *err,uint64_t nr) +{ +DECLARE_HYPERCALL ; + DECLARE_HYPERCALL_BUFFER(struct xen_hvm_set_mem_access_bulk, arg); +DECLARE_HYPERCALL_BOUNCE (arr,sizeof (xen_pfn_t) * nr, +XC_HYPERCALL_BUFFER_BOUNCE_IN ); +DECLARE_HYPERCALL_BOUNCE (err,sizeof (int ) * nr, +XC_HYPERCALL_BUFFER_BOUNCE_OUT ); +int rc; + + arg =xc_hypercall_buffer_alloc (xch, arg,sizeof (*arg)); +if ( arg ==NULL ) + { + PERROR("Could not allocate memory for
xc_hvm_set_mem_access_bulk hypercall");
+ return -1;
+ }
+
+ if ( xc_hypercall_bounce_pre(xch, arr) ) {
+ PERROR("Could not bounce arr for xc_hvm_set_mem_access_bulk
hypercall");
+ rc = -1;
+ goto out;
+ }
+
+ if ( xc_hypercall_bounce_pre(xch, err) ) {
+ PERROR("Could not bounce err for xc_hvm_set_mem_access_bulk
hypercall");
+ rc = -1;
+ goto out;
+ }
+
+ arg->domid = dom; + arg->hvmmem_access = mem_access; + arg->nr = nr; + set_xen_guest_handle(arg->arr, arr); + set_xen_guest_handle(arg->err, err); + + hypercall.op = __HYPERVISOR_hvm_op; + hypercall.arg[0] = HVMOP_set_mem_access_bulk; + hypercall.arg[1] = HYPERCALL_BUFFER_AS_ARG(arg);
+
+ rc = do_xen_hypercall(xch, &hypercall);
+
+out:
+ xc_hypercall_buffer_free(xch, arg); + xc_hypercall_bounce_post(xch, arr); + xc_hypercall_bounce_post(xch, err);
+
+ return rc;
+}
+
int xc_hvm_get_mem_access(
xc_interface *xch, domid_t dom, uint64_t pfn, hvmmem_access_t* mem_access)
{
tools/libxc/xenctrl.h +0 -0
@@ -1568,6 +1568,17 @@ int xc_hvm_set_mem_access( xc_interface *xch, domid_t dom, hvmmem_access_t memaccess,
uint64_t first_pfn, uint64_t nr);
/*
+ * Set the arry of pfns to a specific access.
+ * When a pfn cannot be set to the specified access, its respective field in
+ * @err is set to the corresponding errno value.
+ * Allowed types are HVMMEM_access_default, HVMMEM_access_n, any combination of
+ * HVM_access_ + (rwx), and HVM_access_rx2rw
+ */
+int xc_hvm_set_mem_access_bulk(
+ xc_interface *xch, domid_t dom, hvmmem_access_t memaccess,
+ xen_pfn_t *arr, int *err, uint64_t num);
+
+/*
* Gets the mem access for the given page (returned in memacess on success) */
int xc_hvm_get_mem_access(
xen/arch/x86/hvm/hvm.c +45 -0
51 lines not on this page · raw message
xen/arch/x86/mm/p2m-ept.c +57 -0
62 lines not on this page · raw message
necessary,
* pass a p2m_query_t type along to distinguish. */
@@ -808,6 +865,7 @@ static void ept_change_entry_type_global
void ept_p2m_init(struct p2m_domain *p2m)
{
p2m->set_entry = ept_set_entry;
+ p2m->set_entry_bulk = ept_set_entry_bulk;
p2m->get_entry = ept_get_entry;
p2m->change_entry_type_global = ept_change_entry_type_global;
p2m->audit_p2m = NULL;xen/arch/x86/mm/p2m-pt.c +1 -0
@@ -1139,6 +1139,7 @@ long p2m_pt_audit_p2m(struct p2m_domainvoid p2m_pt_init (struct p2m_domain *p2m) { p2m->set_entry = p2m_set_entry; + p2m->set_entry_bulk =NULL ; p2m->get_entry = p2m_gfn_to_mfn; p2m->change_entry_type_global = p2m_change_type_global; p2m->write_p2m_entry = paging_write_p2m_entry;
xen/arch/x86/mm/p2m.c +36 -0
@@ -1334,6 +1334,42 @@ int p2m_set_mem_access(struct domain *d,return rc; } +int p2m_set_access_bulk (struct domain *d,xen_pfn_t *arr,int *err, +uint64_t nr,hvmmem_access_t access) +{ +struct p2m_domain *p2m =p2m_get_hostp2m (d); +p2m_access_t a; +p2m_access_t memaccess[] = { + p2m_access_n, + p2m_access_r, + p2m_access_w, + p2m_access_rw, + p2m_access_x, + p2m_access_rx, + p2m_access_wx, + p2m_access_rwx, + p2m_access_rx2rw, + p2m_access_n2rwx, + p2m->default_access, + }; +int rc =0 ; + +ASSERT (p2m->set_entry_bulk); + +if ( (unsigned ) access >= HVMMEM_access_default ) +return -EINVAL ; + + a = memaccess[access]; + +p2m_lock (p2m); +if ( p2m->set_entry_bulk (p2m, arr, err, nr,PAGE_ORDER_4K , a) ==0 ) + rc = -ENOMEM ; +p2m_unlock (p2m); + +return rc; +} + +/* Get access type for a pfn * If pfn == -1ul, gets the default access type */ int p2m_get_mem_access(struct domain *d, unsigned long pfn,
xen/include/asm-x86/p2m.h +17 -1
@@ -232,6 +232,13 @@ struct p2m_domain { mfn_t mfn, unsigned int page_order, p2m_type_t p2mt, p2m_access_t p2ma); +int (*set_entry_bulk)(struct p2m_domain *p2m, +xen_pfn_t *arr, +int *err, +uint64_t nr, +unsigned int order, +p2m_access_t p2ma); + mfn_t (*get_entry )(struct p2m_domain *p2m, unsigned long gfn, p2m_type_t *p2mt,
@@ -568,6 +575,11 @@ void p2m_mem_access_resume(struct domainint p2m_set_mem_access (struct domain *d,unsigned long start_pfn,uint32_t nr,hvmmem_access_t access); +/* Set access type for an array of pfns. set_mem_access success or failure is +* returned in the err array. */ +int p2m_set_access_bulk (struct domain *d,xen_pfn_t *arr,int *err, +uint64_t nr,hvmmem_access_t access); +/* Get access type for a pfn * If pfn == -1ul, gets the default access type */ int p2m_get_mem_access(struct domain *d, unsigned long pfn,
@@ -583,7 +595,11 @@ static inline int p2m_set_mem_access(strunsigned long start_pfn, uint32_t nr, hvmmem_access_t access) { return -EINVAL; } -static inline int p2m_get_mem_access (struct domain *d,unsigned long pfn, +static inline int p2m_set_access_bulk (struct domain *d,xen_pfn_t *arr, +int *err,uint64_t nr, +hvmmem_access_t access) +{return -EINVAL ; } +static inline int p2m_get_mem_access (struct domain *d,unsigned long pfn,hvmmem_access_t *access) {return -EINVAL ; }#endif
xen/include/public/hvm/hvm_op.h +18 -0
@@ -261,4 +261,22 @@ DEFINE_XEN_GUEST_HANDLE(xen_hvm_inject_m#endif /* defined(__XEN__) || defined(__XEN_TOOLS__) */ +#if defined(__XEN__) || defined(__XEN_TOOLS__) +#define HVMOP_set_mem_access_bulk 17 +/* Notify that a array of pfns is to have specific access types */ +struct xen_hvm_set_mem_access_bulk { +/* Domain to be updated. */ +domid_t domid; +/* Memory type */ +uint16_t hvmmem_access;/* hvm_access_t */ +/* Array of pfns */ +XEN_GUEST_HANDLE_64 (xen_pfn_t) arr; +XEN_GUEST_HANDLE_64 (int ) err ; +/* Number of entries */ +uint64_t nr; +}; +typedef struct xen_hvm_set_mem_access_bulk xen_hvm_set_mem_access_bulk_t ; +DEFINE_XEN_GUEST_HANDLE (xen_hvm_set_mem_access_bulk_t); +#endif +#endif /* __XEN_PUBLIC_HVM_HVM_OP_H__ */
Quoted 1 line · Aravindh Puthiyaparambil
Does this look correct now?
It addresses the issues I've pointed out, but:
- you should leave the ASSERT where it is, or is there a reason to move it?
- this is wrong:
Quoted 2 lines
- old_entry =
*ept_entry;
+ old_entry->epte =ept_entry->epte;
You should follow the code and see what uses old_entry and you'll see that within the function old_entry->mfn is used (your diff changes the line that uses it) and ept_free_entry also accesses mfn.
- are you sure you can move the ept_sync_domain call past the iommu code?
I made a similar change a while ago, though it is for a more specific case, updating the ept table to "clean" the vram tracking. My change is:
- clear needs_sync when setting the type to logdirty for a leaf entry
if ( !is_epte_present(ept_entry) ||
(!target && p2mt == p2m_ram_logdirty) )
needs_sync = 0;
- only call ept_free_entry in the non-leaf case
if ( target && is_epte_present(&old_entry) )
ept_free_entry(p2m, &old_entry, target);- call ept_sync_domain from hap_clean_vram_tracking
Maybe you can do something similar, for example passing in a hint whether ept_sync_domain needs to be done or not. In my case, the reasoning is that all the entries changed from hap_clean_vram_tracking are leaf entries, so ept_free_entry will never be called and thus ept_sync_domain can be deferred. I didn't think through/consider the iommu case since that code is commented out in my tree.
christian
Quoted 7 lines · Christian Limpach
Quoted 1 line · Aravindh Puthiyaparambil
Does this look correct now?
It addresses the issues I've pointed out, but:
- you should leave the ASSERT where it is, or is there a reason to move
it?
Ok, I will move the ASSERT back to where it was.
Quoted 6 lines
- this is wrong:
xen/arch/x86/mm/p2m-ept.c ept_set_entry(struct p2m_domain *p2m, un · line 350
Quoted 2 lines
xen/arch/x86/mm/p2m-ept.c +1
-1- old_entry = *ept_entry; + old_entry->epte = ept_entry->epte;You should follow the code and see what uses
old_entryand you'll see that within the functionold_entry->mfnis used (your diff changes the line that uses it) andept_free_entryalso accesses mfn.
I will fix that.
Quoted 2 lines
- are you sure you can move the
ept_sync_domaincall past the iommu code?
I was hoping Tim would give me feedback about that.
Quoted 10 lines
I made a similar change a while ago, though it is for a more specific case, updating the ept table to "clean" the vram tracking. My change is:
- clearneeds_syncwhen setting the type to logdirty for a leaf entryif ( !is_epte_present(ept_entry) || (!target && p2mt == p2m_ram_logdirty) ) needs_sync = 0; - only call ept_free_entry in the non-leaf case if ( target && is_epte_present(&old_entry) ) ept_free_entry(p2m, &old_entry, target);
Quoted 9 lines
- call
ept_sync_domainfromhap_clean_vram_trackingMaybe you can do something similar, for example passing in a hint whether
ept_sync_domainneeds to be done or not. In my case, the reasoning is that all the entries changed fromhap_clean_vram_trackingare leaf entries, soept_free_entrywill never be called and thusept_sync_domaincan be deferred. I didn't think through/consider the iommu case since that code is commented out in my tree.
I thought about doing that initially. But then in the bulk case I would always have to call ept_sync_domain() to be on the safe side. But if the iommu case forces me down that path, then I guess I have no choice.
Aravindh
_______________________________________________
Xen-devel mailing list
[email protected]
http://lists.xen.org/xen-devel
Quoted 7 lines · Aravindh Puthiyaparambil
Quoted 6 lines
- this is wrong:
xen/arch/x86/mm/p2m-ept.c ept_set_entry(struct p2m_domain *p2m, un · line 350
Quoted 2 lines
xen/arch/x86/mm/p2m-ept.c +1
-1- old_entry = *ept_entry; + old_entry->epte = ept_entry->epte;You should follow the code and see what uses
old_entryand you'll see that within the functionold_entry->mfnis used (your diff changes the line that uses it) andept_free_entryalso accesses mfn.
Quoted 6 lines
I will fix that.
Quoted 2 lines
- are you sure you can move the
ept_sync_domaincall past the iommu code?I was hoping Tim would give me feedback about that.
I'm afraid I won't be able to review this code until next week. This change is already too late for the 4.2 freeze, though, so there's plenty of time to get it right after we branch.
Cheers,
Tim.
Quoted 10 lines · Tim Deegan
Quoted 9 lines · Aravindh Puthiyaparambil
Quoted 6 lines
- this is wrong:
Quoted 2 lines
- old_entry =
*ept_entry;
+ old_entry->epte =ept_entry->epte;You should follow the code and see what uses
old_entryand you'll see that within the functionold_entry->mfnis used (your diff changes the line that uses it) andept_free_entryalso accesses mfn.I will fix that.
Quoted 8 lines
Quoted 4 lines
Quoted 2 lines
- are you sure you can move the
ept_sync_domaincall past the iommu code?I was hoping Tim would give me feedback about that.
I'm afraid I won't be able to review this code until next week. This change is already too late for the 4.2 freeze, though, so there's plenty of time to get it right after we branch.
Not a problem. This can wait for post 4.2. By then I would have reworked it the way Christian was mentioning.
Thanks,
Aravindh
Quoted 11 lines · Aravindh Puthiyaparambil
Quoted 7 lines · Christian Limpach
Maybe you can do something similar, for example passing in a hint whether
ept_sync_domainneeds to be done or not. In my case, the reasoning is that all the entries changed fromhap_clean_vram_trackingare leaf entries, soept_free_entrywill never be called and thusept_sync_domaincan be deferred. I didn't think through/consider the iommu case since that code is commented out in my tree.
Quoted 3 lines
I thought about doing that initially. But then in the bulk case I would always have to call
ept_sync_domain()to be on the safe side. But if the iommu case forces me down that path, then I guess I have no choice.
I think you should re-consider. It would avoid adding the extra wrapper, which makes this code a lot less readable. Plus it avoids the need for the old_entries array.
Let me re-iterate:
- if it's a leaf entry, then there's no need to call ept_free_entry
- if you don't need to call ept_free_entry, then you can defer ept_sync_domain (subject to the iommu case)
- you could pass in a pointer to a bool to indicate to the caller that ept_sync_domain was deferred and that the caller needs to do it, with a NULL pointer indicating that the caller doesn't support defer
christian
Quoted 21 lines · Christian Limpach
Quoted 11 lines · Aravindh Puthiyaparambil
Quoted 7 lines · Christian Limpach
Maybe you can do something similar, for example passing in a hint whether
ept_sync_domainneeds to be done or not. In my case, the reasoning is that all the entries changed fromhap_clean_vram_trackingare leaf entries, soept_free_entrywill never be called and thusept_sync_domaincan be deferred. I didn't think through/consider the iommu case since that code is commented out in my tree.Quoted 3 lines
I thought about doing that initially. But then in the bulk case I would always have to call
ept_sync_domain()to be on the safe side. But if the iommu case forces me down that path, then I guess I have no choice.I think you should re-consider. It would avoid adding the extra wrapper, which makes this code a lot less readable. Plus it avoids the need for the
old_entriesarray.
Quoted 7 lines
Let me re-iterate:
- if it's a leaf entry, then there's no need to callept_free_entry
- if you don't need to callept_free_entry, then you can deferept_sync_domain(subject to the iommu case)
- you could pass in a pointer to a bool to indicate to the caller thatept_sync_domainwas deferred and that the caller needs to do it, with a NULL pointer indicating that the caller doesn't support defer
I understand now and like this approach. I will rework the patch and send it out.
Thanks,
Aravindh
Quoted 23 lines · Aravindh Puthiyaparambil
Quoted 21 lines · Christian Limpach
Quoted 11 lines · Aravindh Puthiyaparambil
Quoted 7 lines · Christian Limpach
Maybe you can do something similar, for example passing in a hint whether
ept_sync_domainneeds to be done or not. In my case, the reasoning is that all the entries changed fromhap_clean_vram_trackingare leaf entries, soept_free_entrywill never be called and thusept_sync_domaincan be deferred. I didn't think through/consider the iommu case since that code is commented out in my tree.Quoted 3 lines
I thought about doing that initially. But then in the bulk case I would always have to call
ept_sync_domain()to be on the safe side. But if the iommu case forces me down that path, then I guess I have no choice.I think you should re-consider. It would avoid adding the extra wrapper, which makes this code a lot less readable. Plus it avoids the need for the
old_entriesarray.
Quoted 7 lines
Quoted 7 lines
Let me re-iterate:
- if it's a leaf entry, then there's no need to callept_free_entry
- if you don't need to callept_free_entry, then you can deferept_sync_domain(subject to the iommu case)
- you could pass in a pointer to a bool to indicate to the caller thatept_sync_domainwas deferred and that the caller needs to do it, with a NULL pointer indicating that the caller doesn't support defer
How does this look?
changeset: 25257:2c05bdb052ea user: Aravindh Puthiyaparambil <[email protected]> date: Fri Apr 27 20:28:37 2012 -0700 summary: x86/mm: Add sync deferred option to p2m->set_entry()
xen/arch/x86/mm/mem_sharing.c +1 -1
@@ -1272,7 +1272,7 @@ int relinquish_shared_pages(struct domai/* Clear out the p2m entry so no one else may try to * unshare */ p2m->set_entry (p2m, gfn,_mfn (0 ),PAGE_ORDER_4K , - p2m_invalid, p2m_access_rwx); + p2m_invalid, p2m_access_rwx,NULL ); count++; }
xen/arch/x86/mm/p2m-ept.c +12 -1
@@ -274,10 +274,13 @@ static int ept_next_level(struct p2m_dom/* * ept_set_entry() computes 'need_modify_vtd_table' for itself, * by observing whether any gfn->mfn translations are modified. +* If sync_deferred is not NULL, then the caller will take care of +* calling ept_sync_domain() in the cases where it can be deferred. */ static int ept_set_entry (struct p2m_domain *p2m,unsigned long gfn,mfn_t mfn, -unsigned int order,p2m_type_t p2mt,p2m_access_t p2ma) +unsigned int order,p2m_type_t p2mt,p2m_access_t p2ma, +bool_t *sync_deferred) {ept_entry_t *table, *ept_entry =NULL ;unsigned long gfn_remainder = gfn;
@@ -293,6 +296,7 @@ ept_set_entry(struct p2m_domain *p2m, unint needs_sync =1 ;struct domain *d = p2m->domain;ept_entry_t old_entry = { .epte =0 }; +bool_t _sync_deferred =0 ;/* * the caller must make sure:
@@ -309,6 +313,9 @@ ept_set_entry(struct p2m_domain *p2m, un (target ==1 &&hvm_hap_has_2mb (d)) || (target == 0)); +if (sync_deferred) + *sync_deferred =1 ; + table =map_domain_page (ept_get_asr (d));for ( i =ept_get_wl (d); i > target; i-- )
@@ -346,7 +353,11 @@ ept_set_entry(struct p2m_domain *p2m, un/* No need to flush if the old entry wasn't valid */ if ( !is_epte_present (ept_entry) ) + { needs_sync =0 ; +if ( sync_deferred ) + *sync_deferred =0 ; + }/* If we're replacing a non-leaf entry with a leaf entry
(1GiB or 2MiB),
* the intermediate tables will be freed below after the ept flush
@@ -385,6 +396,9 @@ ept_set_entry(struct p2m_domain *p2m, un
ASSERT(is_epte_superpage(ept_entry));
+ if ( sync_deferred )
+ _sync_deferred = 1;
+
split_ept_entry = atomic_read_ept_entry(ept_entry);
if ( !ept_split_super_page(p2m, &split_ept_entry, i, target) )
@@ -438,7 +452,7 @@ ept_set_entry(struct p2m_domain *p2m, un
out:
unmap_domain_page(table);
- if ( needs_sync )
+ if ( needs_sync && !_sync_deferred )
ept_sync_domain(p2m->domain);
if ( rv && iommu_enabled && need_iommu(p2m->domain) &&
need_modify_vtd_table )
@@ -727,7 +741,8 @@ void ept_change_entry_emt_with_range(str
order = level * EPT_TABLE_ORDER;
if ( need_modify_ept_entry(p2m, gfn, mfn,
e.ipat, e.emt, e.sa_p2mt) )- ept_set_entry(p2m, gfn, mfn, order,
e.sa_p2mt, e.access);
+ ept_set_entry(p2m, gfn, mfn, order,
e.sa_p2mt, e.access,
+ NULL);
gfn += trunk;
break;
}@@ -737,7 +752,7 @@ void ept_change_entry_emt_with_range(str
else /* gfn assigned with 4k */
{
if ( need_modify_ept_entry(p2m, gfn, mfn, e.ipat, e.emt,
e.sa_p2mt) )
- ept_set_entry(p2m, gfn, mfn, order, e.sa_p2mt, e.access);
+ ept_set_entry(p2m, gfn, mfn, order, e.sa_p2mt, e.access, NULL);
}
}
p2m_unlock(p2m);xen/arch/x86/mm/p2m-pt.c +2 -1
@@ -291,7 +291,8 @@ p2m_next_level(struct p2m_domain *p2m, m// Returns 0 on error (out of memory) static int p2m_set_entry (struct p2m_domain *p2m,unsigned long gfn,mfn_t mfn, -unsigned int page_order,p2m_type_t p2mt,p2m_access_t p2ma) +unsigned int page_order,p2m_type_t p2mt,p2m_access_t p2ma, +bool_t *unused) {// XXX -- this might be able to be faster iff current->domain == d mfn_t table_mfn =pagetable_get_mfn (p2m_get_pagetable (p2m));
xen/arch/x86/mm/p2m.c +2 -2
@@ -227,7 +227,7 @@ int set_p2m_entry(struct p2m_domain *p2melse order =0 ; -if ( !p2m->set_entry (p2m, gfn, mfn, order, p2mt, p2ma) ) +if ( !p2m->set_entry (p2m, gfn, mfn, order, p2mt, p2ma,NULL ) ) rc =0 ; gfn +=1ul << order;if (mfn_x (mfn) !=INVALID_MFN )
@@ -1199,14 +1199,14 @@ bool_t p2m_mem_access_check(unsigned lonif ( access_w && p2ma == p2m_access_rx2rw ) { - p2m->set_entry (p2m, gfn, mfn,PAGE_ORDER_4K , p2mt, p2m_access_rw); + p2m->set_entry(p2m, gfn, mfn, PAGE_ORDER_4K, p2mt,
p2m_access_rw, NULL);
gfn_unlock(p2m, gfn, 0);
return 1;
}
else if ( p2ma == p2m_access_n2rwx )
{
ASSERT(access_w || access_r || access_x);
- p2m->set_entry(p2m, gfn, mfn, PAGE_ORDER_4K, p2mt, p2m_access_rwx);
+ p2m->set_entry(p2m, gfn, mfn, PAGE_ORDER_4K, p2mt,
p2m_access_rwx, NULL);
}
gfn_unlock(p2m, gfn, 0);@@ -1228,7 +1228,8 @@ bool_t p2m_mem_access_check(unsigned lon
{
/* A listener is not required, so clear the access
restrictions */
gfn_lock(p2m, gfn, 0);
- p2m->set_entry(p2m, gfn, mfn, PAGE_ORDER_4K, p2mt,
p2m_access_rwx);
+ p2m->set_entry(p2m, gfn, mfn, PAGE_ORDER_4K, p2mt,
+ p2m_access_rwx, NULL);
gfn_unlock(p2m, gfn, 0);
}
return 1;
@@ -1292,6 +1293,7 @@ int p2m_set_mem_access(struct domain *d,
p2m_type_t t;
mfn_t mfn;
int rc = 0;+ bool_t sync_deferred = 1;
/* N.B. _not_ static: initializer depends on p2m->default_access */
p2m_access_t memaccess[] = {
@@ -1324,12 +1326,17 @@ int p2m_set_mem_access(struct domain *d,
for ( pfn = start_pfn; pfn < start_pfn + nr; pfn++ )
{
mfn = p2m->get_entry(p2m, pfn, &t, &_a, 0, NULL);
- if ( p2m->set_entry(p2m, pfn, mfn, PAGE_ORDER_4K, t, a) == 0 )
+ if ( p2m->set_entry(p2m, pfn, mfn, PAGE_ORDER_4K, t, a, &sync_deferred)
+ == 0 )
{
rc = -ENOMEM;
break;
}
}+
+ if ( sync_deferred )
+ ept_sync_domain(p2m->domain);
+
p2m_unlock(p2m);
return rc;}
xen/include/asm-x86/p2m.h +2 -1
@@ -231,7 +231,8 @@ struct p2m_domain {unsigned long gfn, mfn_t mfn, unsigned int page_order, p2m_type_t p2mt, - p2m_access_t p2ma); + p2m_access_t p2ma, + bool_t *sync_deferred); mfn_t (*get_entry )(struct p2m_domain *p2m, unsigned long gfn, p2m_type_t *p2mt,
changeset: 25258:5a0d60bb536b user: Aravindh Puthiyaparambil <[email protected]> date: Fri Apr 27 21:10:59 2012 -0700 summary: mem_access: Add xc_hvm_mem_access_bulk() API
tools/libxc/xc_misc.c +16 -0
@@ -570,6 +570,57 @@ int xc_hvm_set_mem_access(return rc; } +int xc_hvm_set_mem_access_bulk ( +xc_interface *xch,domid_t dom,hvmmem_access_t mem_access, +xen_pfn_t *arr,int *err,uint64_t nr) +{ +DECLARE_HYPERCALL ; + DECLARE_HYPERCALL_BUFFER(struct xen_hvm_set_mem_access_bulk, arg); +DECLARE_HYPERCALL_BOUNCE (arr,sizeof (xen_pfn_t) * nr, +XC_HYPERCALL_BUFFER_BOUNCE_IN ); +DECLARE_HYPERCALL_BOUNCE (err,sizeof (int ) * nr, +XC_HYPERCALL_BUFFER_BOUNCE_OUT ); +int rc; + + arg =xc_hypercall_buffer_alloc (xch, arg,sizeof (*arg)); +if ( arg ==NULL ) + { + PERROR("Could not allocate memory for
xc_hvm_set_mem_access_bulk hypercall");
+ return -1;
+ }
+
+ if ( xc_hypercall_bounce_pre(xch, arr) ) {
+ PERROR("Could not bounce arr for xc_hvm_set_mem_access_bulk
hypercall");
+ rc = -1;
+ goto out;
+ }
+
+ if ( xc_hypercall_bounce_pre(xch, err) ) {
+ PERROR("Could not bounce err for xc_hvm_set_mem_access_bulk
hypercall");
+ rc = -1;
+ goto out;
+ }
+
+ arg->domid = dom; + arg->hvmmem_access = mem_access; + arg->nr = nr; + set_xen_guest_handle(arg->arr, arr); + set_xen_guest_handle(arg->err, err); + + hypercall.op = __HYPERVISOR_hvm_op; + hypercall.arg[0] = HVMOP_set_mem_access_bulk; + hypercall.arg[1] = HYPERCALL_BUFFER_AS_ARG(arg);
+
+ rc = do_xen_hypercall(xch, &hypercall);
+
+out:
+ xc_hypercall_buffer_free(xch, arg); + xc_hypercall_bounce_post(xch, arr); + xc_hypercall_bounce_post(xch, err);
+
+ return rc;
+}
+
int xc_hvm_get_mem_access(
xc_interface *xch, domid_t dom, uint64_t pfn, hvmmem_access_t* mem_access)
{
tools/libxc/xenctrl.h +0 -0
@@ -1568,6 +1568,17 @@ int xc_hvm_set_mem_access( xc_interface *xch, domid_t dom, hvmmem_access_t memaccess,
uint64_t first_pfn, uint64_t nr);
/*
+ * Set the arry of pfns to a specific access.
+ * When a pfn cannot be set to the specified access, its respective field in
+ * @err is set to the corresponding errno value.
+ * Allowed types are HVMMEM_access_default, HVMMEM_access_n, any combination of
+ * HVM_access_ + (rwx), and HVM_access_rx2rw
+ */
+int xc_hvm_set_mem_access_bulk(
+ xc_interface *xch, domid_t dom, hvmmem_access_t memaccess,
+ xen_pfn_t *arr, int *err, uint64_t num);
+
+/*
* Gets the mem access for the given page (returned in memacess on success) */
int xc_hvm_get_mem_access(
xen/arch/x86/hvm/hvm.c +45 -0
51 lines not on this page · raw message
xen/arch/x86/mm/p2m.c +55 -0
61 lines not on this page · raw message
xen/include/asm-x86/p2m.h +10 -1
@@ -574,6 +574,11 @@ void p2m_mem_access_resume(struct domainint p2m_set_mem_access (struct domain *d,unsigned long start_pfn,uint32_t nr,hvmmem_access_t access); +/* Set access type for an array of pfns. set_mem_access success or failure is +* returned in the err array. */ +int p2m_set_access_bulk (struct domain *d,xen_pfn_t *arr,int *err, +uint64_t nr,hvmmem_access_t access); +/* Get access type for a pfn * If pfn == -1ul, gets the default access type */ int p2m_get_mem_access(struct domain *d, unsigned long pfn,
@@ -589,7 +594,11 @@ static inline int p2m_set_mem_access(strunsigned long start_pfn, uint32_t nr, hvmmem_access_t access) { return -EINVAL; } -static inline int p2m_get_mem_access (struct domain *d,unsigned long pfn, +static inline int p2m_set_access_bulk (struct domain *d,xen_pfn_t *arr, +int *err,uint64_t nr, +hvmmem_access_t access) +{return -EINVAL ; } +static inline int p2m_get_mem_access (struct domain *d,unsigned long pfn,hvmmem_access_t *access) {return -EINVAL ; }#endif
xen/include/public/hvm/hvm_op.h +18 -0
@@ -261,4 +261,22 @@ DEFINE_XEN_GUEST_HANDLE(xen_hvm_inject_m#endif /* defined(__XEN__) || defined(__XEN_TOOLS__) */ +#if defined(__XEN__) || defined(__XEN_TOOLS__) +#define HVMOP_set_mem_access_bulk 17 +/* Notify that a array of pfns is to have specific access types */ +struct xen_hvm_set_mem_access_bulk { +/* Domain to be updated. */ +domid_t domid; +/* Memory type */ +uint16_t hvmmem_access;/* hvm_access_t */ +/* Array of pfns */ +XEN_GUEST_HANDLE_64 (xen_pfn_t) arr; +XEN_GUEST_HANDLE_64 (int ) err ; +/* Number of entries */ +uint64_t nr; +}; +typedef struct xen_hvm_set_mem_access_bulk xen_hvm_set_mem_access_bulk_t ; +DEFINE_XEN_GUEST_HANDLE (xen_hvm_set_mem_access_bulk_t); +#endif +#endif /* __XEN_PUBLIC_HVM_HVM_OP_H__ */
Quoted 9 lines · Aravindh Puthiyaparambil
Quoted 7 lines
Quoted 7 lines
Let me re-iterate:
- if it's a leaf entry, then there's no need to callept_free_entry
- if you don't need to callept_free_entry, then you can deferept_sync_domain(subject to the iommu case)
- you could pass in a pointer to a bool to indicate to the caller thatept_sync_domainwas deferred and that the caller needs to do it, with a NULL pointer indicating that the caller doesn't support deferHow does this look?
It's missing the "leaf entry" part of my description of how I think things should work. Without that, you're effectively ignoring the following comment at the end of ept_set_entry:
/* Release the old intermediate tables, if any. This has to be the
last thing we do, after the ept_sync_domain() and removal from the iommu tables, so as to avoid a potential use-after-free. */
See inline comments in your patch below for how I'd change this, untested...
christian
xen/arch/x86/mm/p2m-ept.c ept_set_entry(struct p2m_domain *p2m, un · line 299
Quoted 5 lines
xen/arch/x86/mm/p2m-ept.c +1
-0@@ -293,6 +296,7 @@ ept_set_entry(struct p2m_domain *p2m, un int needs_sync = 1; struct domain *d = p2m->domain; ept_entry_t old_entry = { .epte = 0 }; + bool_t _sync_deferred = 0;
don't need that
Quoted 13 lines
xen/arch/x86/mm/p2m-ept.c +0
-0/** the caller must make sure: @@ -309,6 +313,9 @@ ept_set_entry(struct p2m_domain *p2m, un(target == 1 &&
hvm_hap_has_2mb(d)) ||
(target == 0));+ if (
sync_deferred)
+ *sync_deferred = 1;
+
table = map_domain_page(ept_get_asr(d));for ( i =
ept_get_wl(d); i > target; i-- )
xen/arch/x86/mm/p2m-ept.c ept_set_entry(struct p2m_domain *p2m, un · line 356
Quoted 4 lines
xen/arch/x86/mm/p2m-ept.c +0
-0@@ -346,7 +353,11 @@ ept_set_entry(struct p2m_domain *p2m, un /* No need to flush if the old entry wasn't valid */ if ( !is_epte_present(ept_entry) ) needs_sync =0 ;
This should be:
if ( !is_epte_present(ept_entry) ||
(!target && sync_deferred) ) {
needs_sync = 0;
if (sync_deferred)
*sync_deferred = 0;
}This expresses the notion that we're going to skip the call to ept_free_entry since it's a leaf entry (target == 0) and that the caller will make the ept_sync_domain call (sync_deferred != NULL).
xen/arch/x86/mm/p2m.c ept_set_entry(struct p2m_domain *p2m, un · line 401
Quoted 11 lines
xen/arch/x86/mm/p2m.c +3
-0/* If we're replacing a non-leaf entry with a leaf entry(1GiB or 2MiB), * the intermediate tables will be freed below after the ept flush @@ -385,6 +396,9 @@ ept_set_entry(struct p2m_domain *p2m, un ASSERT(is_epte_superpage(ept_entry)); + if ( sync_deferred ) + _sync_deferred = 1; +
don't need that
Quoted 10 lines
(quoted diff) +0
-0split_ept_entry = atomic_read_ept_entry(ept_entry); if ( !ept_split_super_page(p2m, &split_ept_entry, i, target) )@@ -438,7 +452,7 @@ ept_set_entry(struct p2m_domain *p2m, unout:
unmap_domain_page(table);- if (
needs_sync)
+ if (needs_sync&& !_sync_deferred )
ept_sync_domain(p2m->domain);
don't need that change, test on needs_sync is sufficient
Quoted 3 lines
if ( rv &&
iommu_enabled&&need_iommu(p2m->domain)&&need_modify_vtd_table)
Then at the end of ept_set_pte add the test for non-leaf, which is more like an optimization/clarification since ept_free_entry has the same test already.
if ( target && is_epte_present(&old_entry) )
ept_free_entry(p2m, &old_entry, target);Quoted 20 lines · Christian Limpach
Quoted 9 lines · Aravindh Puthiyaparambil
Quoted 7 lines
Quoted 7 lines
Let me re-iterate:
- if it's a leaf entry, then there's no need to callept_free_entry
- if you don't need to callept_free_entry, then you can deferept_sync_domain(subject to the iommu case)
- you could pass in a pointer to a bool to indicate to the caller thatept_sync_domainwas deferred and that the caller needs to do it, with a NULL pointer indicating that the caller doesn't support deferHow does this look?
It's missing the "leaf entry" part of my description of how I think things should work. Without that, you're effectively ignoring the following comment at the end of
ept_set_entry:
/* Release the old intermediate tables, if any. This has to be the
last thing we do, after theept_sync_domain()and removal from the iommu tables, so as to avoid a potential use-after-free. */
Quoted 1 line
See inline comments in your patch below for how I'd change this,
untested...
Quoted 37 lines
christian
Review ept_set_entry(struct p2m_domain *p2m, un · line 299
Quoted 5 lines
(quoted diff) +1
-0@@ -293,6 +296,7 @@ ept_set_entry(struct p2m_domain *p2m, un int needs_sync = 1; struct domain *d = p2m->domain; ept_entry_t old_entry = { .epte = 0 }; + bool_t _sync_deferred = 0;don't need that
Quoted 13 lines
(quoted diff) +3
-0/* * the caller must make sure:@@ -309,6 +313,9 @@ ept_set_entry(struct p2m_domain *p2m, un (target == 1 && hvm_hap_has_2mb(d)) || (target == 0)); + if (sync_deferred) + *sync_deferred = 1; + table = map_domain_page(ept_get_asr(d)); for ( i = ept_get_wl(d); i > target; i-- )Review ept_set_entry(struct p2m_domain *p2m, un · line 356
Quoted 4 lines
(quoted diff) +0
-0@@ -346,7 +353,11 @@ ept_set_entry(struct p2m_domain *p2m, un /* No need to flush if the old entry wasn't valid */ if ( !is_epte_present(ept_entry) ) needs_sync = 0;This should be:
if ( !is_epte_present(ept_entry) || (!target && sync_deferred) ) { needs_sync = 0; if (sync_deferred) *sync_deferred = 0; }
Quoted 8 lines
This expresses the notion that we're going to skip the call to
ept_free_entrysince it's a leaf entry (target == 0) and that the caller will make theept_sync_domaincall (sync_deferred!= NULL).Quoted 4 lines
(quoted diff) +0
-0/* If we're replacing a non-leaf entry with a leaf entry (1GiB or 2MiB), * the intermediate tables will be freed below after the ept
flush
Quoted 27 lines
Review ept_set_entry(struct p2m_domain *p2m, un · line 401
Quoted 7 lines
(quoted diff) +3
-0@@ -385,6 +396,9 @@ ept_set_entry(struct p2m_domain *p2m, un ASSERT(is_epte_superpage(ept_entry)); + if ( sync_deferred ) + _sync_deferred = 1; +don't need that
Review ept_set_entry(struct p2m_domain *p2m, un · line 456
Quoted 10 lines
(quoted diff) +1
-1split_ept_entry = atomic_read_ept_entry(ept_entry); if ( !ept_split_super_page(p2m, &split_ept_entry, i, target) )@@ -438,7 +452,7 @@ ept_set_entry(struct p2m_domain *p2m, un out: unmap_domain_page(table); - if ( needs_sync ) + if ( needs_sync && !_sync_deferred ) ept_sync_domain(p2m->domain);don't need that change, test on
needs_syncis sufficientReview ept_set_entry(struct p2m_domain *p2m, un · line 458
Quoted 3 lines
(quoted diff) +0
-0if ( rv && iommu_enabled && need_iommu(p2m->domain) && need_modify_vtd_table )
Quoted 6 lines
Then at the end of
ept_set_pteadd the test for non-leaf, which is more like an optimization/clarification sinceept_free_entryhas the same test already.if ( target && is_epte_present(&old_entry) ) ept_free_entry(p2m, &old_entry, target);
Sorry for not following and thanks for your patience. Hopefully this looks correct. I have just included the pertinent bit of the patch you were commenting on.
xen/arch/x86/mm/p2m-ept.c Fri May 04 14:56:12 2012 -0700 renamed from xen/arch/x86/mm/p2m-ept.c Fri Apr 27 17:57:55 2012 +0200 +12 -2
@@ -274,10 +274,13 @@ static int ept_next_level(struct p2m_dom /* * ept_set_entry() computes 'need_modify_vtd_table' for itself, * by observing whether any gfn->mfn translations are modified. + * If sync_deferred is not NULL, then the caller will take care of + * calling ept_sync_domain() in the cases where it can be deferred. */ static int ept_set_entry(struct p2m_domain *p2m, unsigned long gfn, mfn_t mfn, - unsigned int order, p2m_type_t p2mt, p2m_access_t p2ma) + unsigned int order, p2m_type_t p2mt, p2m_access_t p2ma, + bool_t *sync_deferred) { ept_entry_t *table, *ept_entry = NULL; unsigned long gfn_remainder = gfn;
@@ -309,6 +312,9 @@ ept_set_entry(struct p2m_domain *p2m, un (target == 1 && hvm_hap_has_2mb(d)) || (target == 0)); + if (sync_deferred) + *sync_deferred = 1; + table = map_domain_page(ept_get_asr(d)); for ( i = ept_get_wl(d); i > target; i-- )
@@ -345,8 +351,12 @@ ept_set_entry(struct p2m_domain *p2m, un ept_entry_t new_entry = { .epte = 0 }; /* No need to flush if the old entry wasn't valid */ - if ( !is_epte_present(ept_entry) ) + if ( !is_epte_present(ept_entry) || (!target && sync_deferred) ) + { needs_sync = 0; + if ( sync_deferred ) + *sync_deferred = 0; + } /* If we're replacing a non-leaf entry with a leaf entry (1GiB or
2MiB),
* the intermediate tables will be freed below after the ept flush
@@ -477,7 +487,7 @@ out:
last thing we do, after the ept_sync_domain() and removal from the iommu tables, so as to avoid a potential use-after-free. */
- if ( is_epte_present(&old_entry) )
+ if ( target && is_epte_present(&old_entry) )
ept_free_entry(p2m, &old_entry, target);
return rv;_______________________________________________
Xen-devel mailing list
[email protected]
http://lists.xen.org/xen-devel
Hi Aravindh,
Quoted 19 lines · Aravindh Puthiyaparambil
diff -r
9dda0efd8ce1-rc180942992bfxen/arch/x86/mm/p2m-ept.cxen/arch/x86/mm/p2m-ept.c Fri May 04 14:56:12 2012 -0700 renamed from xen/arch/x86/mm/p2m-ept.c Fri Apr 27 17:57:55 2012 +0200 +4
-1@@ -274,10 +274,13 @@ static int ept_next_level(struct p2m_dom /* * ept_set_entry() computes 'need_modify_vtd_table' for itself, * by observing whether any gfn->mfn translations are modified. + * If sync_deferred is not NULL, then the caller will take care of + * calling ept_sync_domain() in the cases where it can be deferred. */ static int ept_set_entry(struct p2m_domain *p2m, unsigned long gfn, mfn_t mfn, - unsigned int order, p2m_type_t p2mt, p2m_access_t p2ma) + unsigned int order, p2m_type_t p2mt, p2m_access_t p2ma, + bool_t *sync_deferred) { ept_entry_t *table, *ept_entry = NULL; unsigned long gfn_remainder = gfn;
xen/arch/x86/mm/p2m-ept.c Fri May 04 14:56:12 2012 -0700 ept_set_entry(struct p2m_domain *p2m, un · line 315
Quoted 4 lines
xen/arch/x86/mm/p2m-ept.c Fri May 04 14:56:12 2012 -0700 renamed from xen/arch/x86/mm/p2m-ept.c Fri Apr 27 17:57:55 2012 +0200 +1
-0@@ -309,6 +312,9 @@ ept_set_entry(struct p2m_domain *p2m, un (target == 1 && hvm_hap_has_2mb(d)) || (target == 0)); + if (sync_deferred)
Xen coding style has spaces inside those parentheses.
Quoted 6 lines
xen/arch/x86/mm/p2m-ept.c Fri May 04 14:56:12 2012 -0700 renamed from xen/arch/x86/mm/p2m-ept.c Fri Apr 27 17:57:55 2012 +0200 +2
-0+ *sync_deferred = 1; + table = map_domain_page(ept_get_asr(d)); for ( i = ept_get_wl(d); i > target; i-- )
xen/arch/x86/mm/p2m-ept.c Fri May 04 14:56:12 2012 -0700 ept_set_entry(struct p2m_domain *p2m, un · line 358
Quoted 9 lines
xen/arch/x86/mm/p2m-ept.c Fri May 04 14:56:12 2012 -0700 renamed from xen/arch/x86/mm/p2m-ept.c Fri Apr 27 17:57:55 2012 +0200 +4
-1@@ -345,8 +351,12 @@ ept_set_entry(struct p2m_domain *p2m, un ept_entry_t new_entry = { .epte = 0 }; /* No need to flush if the old entry wasn't valid */ - if ( !is_epte_present(ept_entry) ) + if ( !is_epte_present(ept_entry) || (!target && sync_deferred) ) + { needs_sync = 0; + if ( sync_deferred ) + *sync_deferred = 0;
I don't think you should do this -- you call this function in a loop and you may need to sync because of an earlier iteration. Better to only write a 1 to *sync_deferred once you know there's a sync to be done, and never to write a zero.
xen/arch/x86/mm/p2m-ept.c Fri May 04 14:56:12 2012 -0700 ept_set_entry(struct p2m_domain *p2m, un · line 359
Quoted 1 line
xen/arch/x86/mm/p2m-ept.c Fri May 04 14:56:12 2012 -0700 renamed from xen/arch/x86/mm/p2m-ept.c Fri Apr 27 17:57:55 2012 +0200 +1
-0+ }
One comment on the rest of the patch: your calling function calls ept_sync_domain() directly if sync_deferred == 1. That's correct for now but it would be cleaner to use a sync() function pointer in the struct p2m_domain, the same as the other implementation-specific calls.
Other than that, I think this looks pretty good to go in after 4.2 has branched.
Cheers,
Tim.
Quoted 37 lines · Tim Deegan
Hi Aravindh,
Quoted 19 lines · Aravindh Puthiyaparambil
diff -r
9dda0efd8ce1-rc180942992bfxen/arch/x86/mm/p2m-ept.cxen/arch/x86/mm/p2m-ept.c Fri May 04 14:56:12 2012 -0700 renamed from xen/arch/x86/mm/p2m-ept.c Fri Apr 27 17:57:55 2012 +0200 +4
-1@@ -274,10 +274,13 @@ static int ept_next_level(struct p2m_dom /* * ept_set_entry() computes 'need_modify_vtd_table' for itself, * by observing whether any gfn->mfn translations are modified. + * If sync_deferred is not NULL, then the caller will take care of + * calling ept_sync_domain() in the cases where it can be deferred. */ static int ept_set_entry(struct p2m_domain *p2m, unsigned long gfn, mfn_t mfn, - unsigned int order, p2m_type_t p2mt, p2m_access_t p2ma) + unsigned int order, p2m_type_t p2mt, p2m_access_t p2ma, + bool_t *sync_deferred) { ept_entry_t *table, *ept_entry = NULL; unsigned long gfn_remainder = gfn;xen/arch/x86/mm/p2m-ept.c Fri May 04 14:56:12 2012 -0700 ept_set_entry(struct p2m_domain *p2m, un · line 315
Quoted 4 lines
xen/arch/x86/mm/p2m-ept.c Fri May 04 14:56:12 2012 -0700 renamed from xen/arch/x86/mm/p2m-ept.c Fri Apr 27 17:57:55 2012 +0200 +1
-0@@ -309,6 +312,9 @@ ept_set_entry(struct p2m_domain *p2m, un (target == 1 && hvm_hap_has_2mb(d)) || (target == 0)); + if (sync_deferred)Xen coding style has spaces inside those parentheses.
Quoted 8 lines
xen/arch/x86/mm/p2m-ept.c Fri May 04 14:56:12 2012 -0700 renamed from xen/arch/x86/mm/p2m-ept.c Fri Apr 27 17:57:55 2012 +0200 +2
-0+ *sync_deferred = 1; + table = map_domain_page(ept_get_asr(d)); for ( i = ept_get_wl(d); i > target; i-- )@@ -345,8 +351,12 @@ ept_set_entry(struct p2m_domain *p2m, un ept_entry_t new_entry = { .epte = 0 };
Quoted 13 lines
xen/arch/x86/mm/p2m-ept.c Fri May 04 14:56:12 2012 -0700 ept_set_entry(struct p2m_domain *p2m, un · line 358
Quoted 8 lines
xen/arch/x86/mm/p2m-ept.c Fri May 04 14:56:12 2012 -0700 renamed from xen/arch/x86/mm/p2m-ept.c Fri Apr 27 17:57:55 2012 +0200 +4
-1/* No need to flush if the old entry wasn't valid */ - if ( !is_epte_present(ept_entry) ) + if ( !is_epte_present(ept_entry) || (!target && sync_deferred) ) + { needs_sync = 0; + if ( sync_deferred ) + *sync_deferred = 0;I don't think you should do this -- you call this function in a loop and you may need to sync because of an earlier iteration. Better to only write a 1 to
*sync_deferredonce you know there's a sync to be done, and never to write a zero.
You are right. I will fix that.
Quoted 6 lines
xen/arch/x86/mm/p2m-ept.c Fri May 04 14:56:12 2012 -0700 ept_set_entry(struct p2m_domain *p2m, un · line 359
Quoted 1 line
xen/arch/x86/mm/p2m-ept.c Fri May 04 14:56:12 2012 -0700 renamed from xen/arch/x86/mm/p2m-ept.c Fri Apr 27 17:57:55 2012 +0200 +1
-0+ }One comment on the rest of the patch: your calling function calls
ept_sync_domain()directly ifsync_deferred== 1. That's correct for now but it would be cleaner to use async()function pointer in the structp2m_domain, the same as the other implementation-specific calls.
I can add that too.
Quoted 2 lines
Other than that, I think this looks pretty good to go in after 4.2 has branched.
OK, I will resubmit once 4.2 has branched.
Thanks,
Aravindh
_______________________________________________
Xen-devel mailing list
[email protected]
http://lists.xen.org/xen-devel