diff --git a/xen/common/domain.c b/xen/common/domain.c index 6b71c6d6a9..14b1341e53 100644 --- a/xen/common/domain.c +++ b/xen/common/domain.c @@ -412,11 +412,18 @@ static int domain_teardown(struct domain *d) v = d->teardown.vcpu enum { - PROG_vcpu_teardown = 1, + PROG_none, + PROG_gnttab_mappings, + PROG_vcpu_teardown, PROG_done, }; - case 0: + case PROG_none: + rc = gnttab_release_mappings(d); + if ( rc ) + return rc; + + PROGRESS(gnttab_mappings): for_each_vcpu ( d, v ) { PROGRESS_VCPU(teardown); @@ -908,7 +915,6 @@ int domain_kill(struct domain *d) return domain_kill(d); d->is_dying = DOMDYING_dying; argo_destroy(d); - gnttab_release_mappings(d); vnuma_destroy(d->vnuma); domain_set_outstanding_pages(d, 0); /* fallthrough */ diff --git a/xen/common/grant_table.c b/xen/common/grant_table.c index fab77ab9cc..17cce0154a 100644 --- a/xen/common/grant_table.c +++ b/xen/common/grant_table.c @@ -64,7 +64,13 @@ struct grant_table { unsigned int nr_grant_frames; /* Number of grant status frames shared with guest (for version 2) */ unsigned int nr_status_frames; - /* Number of available maptrack entries. */ + /* + * Number of available maptrack entries. For cleanup purposes it is + * important to realize that this field and @maptrack further down will + * only ever be accessed by the local domain. Thus it is okay to clean + * up early, and to shrink the limit for the purpose of tracking cleanup + * progress. + */ unsigned int maptrack_limit; /* Shared grant table (see include/public/grant_table.h). */ union { @@ -3679,9 +3685,7 @@ do_grant_table_op( #include "compat/grant_table.c" #endif -void -gnttab_release_mappings( - struct domain *d) +int gnttab_release_mappings(struct domain *d) { struct grant_table *gt = d->grant_table, *rgt; struct grant_mapping *map; @@ -3695,8 +3699,32 @@ gnttab_release_mappings( BUG_ON(!d->is_dying); - for ( handle = 0; handle < gt->maptrack_limit; handle++ ) + if ( !gt || !gt->maptrack ) + return 0; + + for ( handle = gt->maptrack_limit; handle; ) { + /* + * Deal with full pages such that their freeing (in the body of the + * if()) remains simple. + */ + if ( handle < gt->maptrack_limit && !(handle % MAPTRACK_PER_PAGE) ) + { + /* + * Changing maptrack_limit alters nr_maptrack_frames()'es return + * value. Free the then excess trailing page right here, rather + * than leaving it to grant_table_destroy() (and in turn requiring + * to leave gt->maptrack_limit unaltered). + */ + gt->maptrack_limit = handle; + FREE_XENHEAP_PAGE(gt->maptrack[nr_maptrack_frames(gt)]); + + if ( hypercall_preempt_check() ) + return -ERESTART; + } + + --handle; + map = &maptrack_entry(gt, handle); if ( !(map->flags & (GNTMAP_device_map|GNTMAP_host_map)) ) continue; @@ -3780,6 +3808,11 @@ gnttab_release_mappings( map->flags = 0; } + + gt->maptrack_limit = 0; + FREE_XENHEAP_PAGE(gt->maptrack[0]); + + return 0; } void grant_table_warn_active_grants(struct domain *d) @@ -3843,8 +3876,7 @@ grant_table_destroy( free_xenheap_page(t->shared_raw[i]); xfree(t->shared_raw); - for ( i = 0; i < nr_maptrack_frames(t); i++ ) - free_xenheap_page(t->maptrack[i]); + ASSERT(!t->maptrack_limit); vfree(t->maptrack); for ( i = 0; i < nr_active_grant_frames(t); i++ ) diff --git a/xen/include/xen/grant_table.h b/xen/include/xen/grant_table.h index 9f8b7e66c1..41713e2726 100644 --- a/xen/include/xen/grant_table.h +++ b/xen/include/xen/grant_table.h @@ -47,9 +47,7 @@ void grant_table_init_vcpu(struct vcpu *v); void grant_table_warn_active_grants(struct domain *d); /* Domain death release of granted mappings of other domains' memory. */ -void -gnttab_release_mappings( - struct domain *d); +int gnttab_release_mappings(struct domain *d); int mem_sharing_gref_to_gfn(struct grant_table *gt, grant_ref_t ref, gfn_t *gfn, uint16_t *status); @@ -80,7 +78,7 @@ static inline void grant_table_init_vcpu(struct vcpu *v) {} static inline void grant_table_warn_active_grants(struct domain *d) {} -static inline void gnttab_release_mappings(struct domain *d) {} +static inline int gnttab_release_mappings(struct domain *d) { return 0; } static inline int mem_sharing_gref_to_gfn(struct grant_table *gt, grant_ref_t ref,