diff --git a/libsurfman/src/project.h b/libsurfman/src/project.h index 6f2dd01..757013d 100644 --- a/libsurfman/src/project.h +++ b/libsurfman/src/project.h @@ -50,6 +50,8 @@ # include # include +# define XC_WANT_COMPAT_MAP_FOREIGN_API +# define XC_WANT_COMPAT_DEVICEMODEL_API # include # include # include diff --git a/libsurfman/src/surface.c b/libsurfman/src/surface.c index 6731954..0059e71 100644 --- a/libsurfman/src/surface.c +++ b/libsurfman/src/surface.c @@ -78,6 +78,24 @@ static void surface_update_mfn_list (surfman_surface_t * surface) free(pfns); } +static void surface_update_mfn_arr (surfman_surface_t * surface) +{ + struct surface_priv *p = PRIV(surface); + xen_pfn_t *pfns; + int rc; + int domid = surface->pages_domid; + + assert(p->type == TYPE_PFN_ARR); + + pfns = xcalloc(surface->page_count, sizeof (xen_pfn_t)); + memcpy(pfns, p->u.pfn_arr.arr, surface->page_count * sizeof (xen_pfn_t)); + + if (xc_translate_gpfn_to_mfn (domid, surface->page_count, pfns, surface->mfns)) + surfman_error ("Failed to translate gpfns for dom%d.", domid); + + free(pfns); +} + static void update_mapping (struct surface_priv *p, size_t len) { size_t npages = (len + XC_PAGE_SIZE - 1) / XC_PAGE_SIZE; @@ -97,23 +115,15 @@ static void update_mapping (struct surface_priv *p, size_t len) p->u.mmap.offset); break; case TYPE_PFN_ARR: - pfns = malloc (npages * sizeof (*pfns)); - if (!pfns) - break; - - for (i = 0; i < npages; i++) - pfns[i] = p->u.pfn_arr.arr[i]; - + pfns = xcalloc (npages, sizeof (*pfns)); + memcpy(pfns, p->u.pfn_arr.arr, npages * sizeof (*pfns)); p->baseptr = xc_mmap_foreign (p->baseptr, len, PROT_READ | PROT_WRITE, p->u.pfn_arr.domid, pfns); free (pfns); break; case TYPE_PFN_LINEAR: - pfns = malloc (npages * sizeof (*pfns)); - if (!pfns) - break; - + pfns = xcalloc (npages, sizeof (*pfns)); for (i = 0; i < npages; i++) pfns[i] = p->u.pfn_linear.base + i; @@ -135,6 +145,59 @@ static void update_mapping (struct surface_priv *p, size_t len) p->len = len; } +static int compare_pfns(const void *a, const void *b) +{ + const xen_pfn_t *pa = a, *pb = b; + + return *pa - *pb; +} + +/* + * Pin cache-attributes of a pfn range from a guest. + * On guests without auto-translation (full PV), this will return an error. + * While it may seem overkill to qsort() the pfns, there is no guaranty that + * this is a contiguous range of pfns. Most likely it is not, a virtual + * framebuffer will be allocated using vmalloc(). + * xc_domain_pin_memory_cacheattr() is expensive though and pinning each page + * on its own will cause significant slow down at guest video initialization. + * Sorting the pfns and pinning contiguous ranges tries to mitigate that. + */ +static int surface_pin_cacheattr(surfman_surface_t * surface, + unsigned int cacheattr) +{ + struct surface_priv *p = PRIV(surface); + xen_pfn_t *pfns; + size_t i, j; + int rc; + + pfns = malloc (surface->page_count * sizeof (pfns[0])); + if (!pfns) + return -ENOMEM; + + memcpy(pfns, p->u.pfn_arr.arr, surface->page_count * sizeof (pfns[0])); + + qsort (pfns, surface->page_count, sizeof (pfns[0]), &compare_pfns); + for (i = 0; i < surface->page_count; i = j) + { + for (j = i + 1; + (j < surface->page_count) && (pfns[j] == (pfns[j - 1] + 1)); + ++j) + continue; + + surfman_debug ("Pinning cacheattr on dom%u %#lx-%#lx.", + surface->pages_domid, pfns[i], pfns[j - 1]); + rc = xc_hvm_pin_memory_cacheattr (surface->pages_domid, + pfns[i], pfns[j - 1], + XC_MAP_CACHEATTR_WC); + if (rc) + surfman_warning ("Failed to change cache attributes for dom%u %#lx-%#lx (%s).", + surface->pages_domid, pfns[i], pfns[j - 1], strerror(-rc)); + } + free (pfns); + + return 0; +} + void *surface_map (surfman_surface_t * surface) { struct surface_priv *p = PRIV(surface); @@ -213,6 +276,9 @@ void surfman_surface_update_pfn_arr (surfman_surface_t * surface, const xen_pfn_t * pfns) { struct surface_priv *p = PRIV(surface); + xc_dominfo_t info; + xen_pfn_t *_pfns; + size_t i, j; pthread_mutex_lock (&p->lock); p->type = TYPE_PFN_ARR; @@ -220,6 +286,16 @@ void surfman_surface_update_pfn_arr (surfman_surface_t * surface, p->u.pfn_arr.arr = pfns; if (p->baseptr) update_mapping (p, surface->page_count * XC_PAGE_SIZE); + + if (xc_domid_getinfo (surface->pages_domid, &info) != 1) + surfman_error ("Cannot retrieve dom%u information.", surface->pages_domid); + else if (info.hvm) + { + if (surface_pin_cacheattr (surface, XC_MAP_CACHEATTR_WC)) + surfman_error ("Failed to pin dom%u surface cache-attributes.", surface->pages_domid); + surface_update_mfn_arr (surface); + } + pthread_mutex_unlock (&p->lock); } diff --git a/libsurfman/src/surfman.h b/libsurfman/src/surfman.h index f09f2c6..bc7369a 100644 --- a/libsurfman/src/surfman.h +++ b/libsurfman/src/surfman.h @@ -618,6 +618,7 @@ int xc_domid_exists(int domid); int xc_domid_getinfo(int domid, xc_dominfo_t *info); void *xc_mmap_foreign(void *addr, size_t length, int prot, int domid, xen_pfn_t *pages); int xc_hvm_get_dirty_vram(int domid, uint64_t base_pfn, size_t n, unsigned long *db); +int xc_hvm_pin_memory_cacheattr(int domid, uint64_t pfn_start, uint64_t pfn_end, uint32_t type); /* configfile.c */ const char *config_get(const char *prefix, const char *key); const char *config_dump(void); diff --git a/libsurfman/src/xc.c b/libsurfman/src/xc.c index f18ad2a..97bf118 100644 --- a/libsurfman/src/xc.c +++ b/libsurfman/src/xc.c @@ -54,32 +54,14 @@ int xc_domid_getinfo(int domid, xc_dominfo_t *info) void *xc_mmap_foreign(void *addr, size_t length, int prot, int domid, xen_pfn_t *pages) { - void *ret; - int rc; - privcmd_mmapbatch_t ioctlx; - size_t i; - - ret = mmap (addr, length, prot, MAP_SHARED, privcmd_fd, 0); - if (ret == MAP_FAILED) - return ret; - - ioctlx.num = (length + XC_PAGE_SIZE - 1) / XC_PAGE_SIZE; - ioctlx.dom = domid; - ioctlx.addr = (unsigned long)ret; - ioctlx.arr = pages; + (void) addr; /* This is munmaped in update_mappings. */ - rc = ioctl(privcmd_fd, IOCTL_PRIVCMD_MMAPBATCH, &ioctlx); - - for (i = 0; i < length; i += XC_PAGE_SIZE) - pages[i >> XC_PAGE_SHIFT] &= ~XEN_DOMCTL_PFINFO_LTAB_MASK; - - if (rc < 0) - { - munmap(ret, length); - ret = MAP_FAILED; - } + assert(xch != NULL); + assert(length > 0); + assert(pages != NULL); /* It's actually an array of pfns... */ - return ret; + return xc_map_foreign_pages (xch, domid, prot, pages, + (length + XC_PAGE_SIZE - 1 >> XC_PAGE_SHIFT)); } int xc_translate_gpfn_to_mfn (int domid, size_t pfn_count, @@ -111,3 +93,8 @@ int xc_hvm_get_dirty_vram(int domid, uint64_t base_pfn, size_t n, { return xc_hvm_track_dirty_vram (xch, domid, base_pfn, n, db); } + +int xc_hvm_pin_memory_cacheattr(int domid, uint64_t pfn_start, uint64_t pfn_end, uint32_t type) +{ + return xc_domain_pin_memory_cacheattr(xch, domid, pfn_start, pfn_end, type); +}