Skip to content

Commit

Permalink
xenbus: Support multiple grants ring with 64KB
Browse files Browse the repository at this point in the history
The PV ring may use multiple grants and expect them to be mapped
contiguously in the virtual memory.

Although, the current code is relying on a Linux page will be mapped to
a single grant. On build where Linux is using a different page size than
the grant (i.e other than 4KB), the grant will always be mapped on the
first 4KB of each Linux page which make the final ring not contiguous in
the memory.

This can be fixed by mapping multiple grant in a same Linux page.

Signed-off-by: Julien Grall <julien.grall@citrix.com>
Signed-off-by: David Vrabel <david.vrabel@citrix.com>
  • Loading branch information
Julien Grall authored and David Vrabel committed Oct 23, 2015
1 parent f73314b commit 89bf4b4
Showing 1 changed file with 72 additions and 25 deletions.
97 changes: 72 additions & 25 deletions drivers/xen/xenbus/xenbus_client.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,19 @@

#include "xenbus_probe.h"

#define XENBUS_PAGES(_grants) (DIV_ROUND_UP(_grants, XEN_PFN_PER_PAGE))

#define XENBUS_MAX_RING_PAGES (XENBUS_PAGES(XENBUS_MAX_RING_GRANTS))

struct xenbus_map_node {
struct list_head next;
union {
struct {
struct vm_struct *area;
} pv;
struct {
struct page *pages[XENBUS_MAX_RING_GRANTS];
struct page *pages[XENBUS_MAX_RING_PAGES];
unsigned long addrs[XENBUS_MAX_RING_GRANTS];
void *addr;
} hvm;
};
Expand Down Expand Up @@ -591,19 +596,42 @@ static int xenbus_map_ring_valloc_pv(struct xenbus_device *dev,
return err;
}

struct map_ring_valloc_hvm
{
unsigned int idx;

/* Why do we need two arrays? See comment of __xenbus_map_ring */
phys_addr_t phys_addrs[XENBUS_MAX_RING_GRANTS];
unsigned long addrs[XENBUS_MAX_RING_GRANTS];
};

static void xenbus_map_ring_setup_grant_hvm(unsigned long gfn,
unsigned int goffset,
unsigned int len,
void *data)
{
struct map_ring_valloc_hvm *info = data;
unsigned long vaddr = (unsigned long)gfn_to_virt(gfn);

info->phys_addrs[info->idx] = vaddr;
info->addrs[info->idx] = vaddr;

info->idx++;
}

static int xenbus_map_ring_valloc_hvm(struct xenbus_device *dev,
grant_ref_t *gnt_ref,
unsigned int nr_grefs,
void **vaddr)
{
struct xenbus_map_node *node;
int i;
int err;
void *addr;
bool leaked = false;
/* Why do we need two arrays? See comment of __xenbus_map_ring */
phys_addr_t phys_addrs[XENBUS_MAX_RING_GRANTS];
unsigned long addrs[XENBUS_MAX_RING_GRANTS];
struct map_ring_valloc_hvm info = {
.idx = 0,
};
unsigned int nr_pages = XENBUS_PAGES(nr_grefs);

if (nr_grefs > XENBUS_MAX_RING_GRANTS)
return -EINVAL;
Expand All @@ -614,24 +642,22 @@ static int xenbus_map_ring_valloc_hvm(struct xenbus_device *dev,
if (!node)
return -ENOMEM;

err = alloc_xenballooned_pages(nr_grefs, node->hvm.pages);
err = alloc_xenballooned_pages(nr_pages, node->hvm.pages);
if (err)
goto out_err;

for (i = 0; i < nr_grefs; i++) {
unsigned long pfn = page_to_pfn(node->hvm.pages[i]);
phys_addrs[i] = (unsigned long)pfn_to_kaddr(pfn);
addrs[i] = (unsigned long)pfn_to_kaddr(pfn);
}
gnttab_foreach_grant(node->hvm.pages, nr_grefs,
xenbus_map_ring_setup_grant_hvm,
&info);

err = __xenbus_map_ring(dev, gnt_ref, nr_grefs, node->handles,
phys_addrs, GNTMAP_host_map, &leaked);
info.phys_addrs, GNTMAP_host_map, &leaked);
node->nr_handles = nr_grefs;

if (err)
goto out_free_ballooned_pages;

addr = vmap(node->hvm.pages, nr_grefs, VM_MAP | VM_IOREMAP,
addr = vmap(node->hvm.pages, nr_pages, VM_MAP | VM_IOREMAP,
PAGE_KERNEL);
if (!addr) {
err = -ENOMEM;
Expand All @@ -649,14 +675,13 @@ static int xenbus_map_ring_valloc_hvm(struct xenbus_device *dev,

out_xenbus_unmap_ring:
if (!leaked)
xenbus_unmap_ring(dev, node->handles, node->nr_handles,
addrs);
xenbus_unmap_ring(dev, node->handles, nr_grefs, info.addrs);
else
pr_alert("leaking %p size %u page(s)",
addr, nr_grefs);
addr, nr_pages);
out_free_ballooned_pages:
if (!leaked)
free_xenballooned_pages(nr_grefs, node->hvm.pages);
free_xenballooned_pages(nr_pages, node->hvm.pages);
out_err:
kfree(node);
return err;
Expand Down Expand Up @@ -782,13 +807,33 @@ static int xenbus_unmap_ring_vfree_pv(struct xenbus_device *dev, void *vaddr)
return err;
}

struct unmap_ring_vfree_hvm
{
unsigned int idx;
unsigned long addrs[XENBUS_MAX_RING_GRANTS];
};

static void xenbus_unmap_ring_setup_grant_hvm(unsigned long gfn,
unsigned int goffset,
unsigned int len,
void *data)
{
struct unmap_ring_vfree_hvm *info = data;

info->addrs[info->idx] = (unsigned long)gfn_to_virt(gfn);

info->idx++;
}

static int xenbus_unmap_ring_vfree_hvm(struct xenbus_device *dev, void *vaddr)
{
int rv;
struct xenbus_map_node *node;
void *addr;
unsigned long addrs[XENBUS_MAX_RING_GRANTS];
int i;
struct unmap_ring_vfree_hvm info = {
.idx = 0,
};
unsigned int nr_pages;

spin_lock(&xenbus_valloc_lock);
list_for_each_entry(node, &xenbus_valloc_pages, next) {
Expand All @@ -808,18 +853,20 @@ static int xenbus_unmap_ring_vfree_hvm(struct xenbus_device *dev, void *vaddr)
return GNTST_bad_virt_addr;
}

for (i = 0; i < node->nr_handles; i++)
addrs[i] = (unsigned long)pfn_to_kaddr(page_to_pfn(node->hvm.pages[i]));
nr_pages = XENBUS_PAGES(node->nr_handles);

gnttab_foreach_grant(node->hvm.pages, node->nr_handles,
xenbus_unmap_ring_setup_grant_hvm,
&info);

rv = xenbus_unmap_ring(dev, node->handles, node->nr_handles,
addrs);
info.addrs);
if (!rv) {
vunmap(vaddr);
free_xenballooned_pages(node->nr_handles, node->hvm.pages);
free_xenballooned_pages(nr_pages, node->hvm.pages);
}
else
WARN(1, "Leaking %p, size %u page(s)\n", vaddr,
node->nr_handles);
WARN(1, "Leaking %p, size %u page(s)\n", vaddr, nr_pages);

kfree(node);
return rv;
Expand Down

0 comments on commit 89bf4b4

Please sign in to comment.