diff --git a/xen/common/domain.c b/xen/common/domain.c index ee9c604118..bd28f15f83 100644 --- a/xen/common/domain.c +++ b/xen/common/domain.c @@ -776,11 +776,13 @@ 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 */ case DOMDYING_dying: + rc = gnttab_release_mappings(d); + if ( rc ) + break; rc = evtchn_destroy(d); if ( rc ) break; diff --git a/xen/common/grant_table.c b/xen/common/grant_table.c index b15e978d06..cf37f0d532 100644 --- a/xen/common/grant_table.c +++ b/xen/common/grant_table.c @@ -63,7 +63,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 { @@ -3675,9 +3681,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; @@ -3691,10 +3695,34 @@ 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; ) { unsigned int clear_flags = 0; + /* + * 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; @@ -3785,6 +3813,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) @@ -3848,8 +3881,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 98603604b8..ab4726bdc4 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); @@ -78,7 +76,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,