Skip to content

Commit

Permalink
Merge pull request #30 from eric-ch/s9-oxt-1650
Browse files Browse the repository at this point in the history
S9: OXT-1650: Support vfb from HVM or PVH guests.
  • Loading branch information
jean-edouard authored Jul 25, 2019
2 parents b0b03fc + 035ff0f commit 5d286ba
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 35 deletions.
2 changes: 2 additions & 0 deletions libsurfman/src/project.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@
# include <sys/types.h>
# include <sys/ioctl.h>

# define XC_WANT_COMPAT_MAP_FOREIGN_API
# define XC_WANT_COMPAT_DEVICEMODEL_API
# include <xenctrl.h>
# include <xen/sys/privcmd.h>
# include <xen/hvm/ioreq.h>
Expand Down
98 changes: 87 additions & 11 deletions libsurfman/src/surface.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;

Expand All @@ -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);
Expand Down Expand Up @@ -213,13 +276,26 @@ 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;
p->u.pfn_arr.domid = surface->pages_domid;
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);
}

Expand Down
1 change: 1 addition & 0 deletions libsurfman/src/surfman.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
35 changes: 11 additions & 24 deletions libsurfman/src/xc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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);
}

0 comments on commit 5d286ba

Please sign in to comment.