Commit 7050096d authored by Juergen Gross's avatar Juergen Gross
Browse files

xen/xenbus: add xenbus_setup_ring() service function



Most PV device frontends share very similar code for setting up shared
ring buffers:

- allocate page(s)
- init the ring admin data
- give the backend access to the ring via grants

Tearing down the ring requires similar actions in all frontends again:

- remove grants
- free the page(s)

Provide service functions xenbus_setup_ring() and xenbus_teardown_ring()
for that purpose.

Signed-off-by: default avatarJuergen Gross <jgross@suse.com>
Reviewed-by: default avatarBoris Ostrovsky <boris.ostrovsky@oracle.com>
Signed-off-by: default avatarJuergen Gross <jgross@suse.com>
parent 6fac592c
Loading
Loading
Loading
Loading
+69 −0
Original line number Diff line number Diff line
@@ -407,6 +407,75 @@ int xenbus_grant_ring(struct xenbus_device *dev, void *vaddr,
}
EXPORT_SYMBOL_GPL(xenbus_grant_ring);

/*
 * xenbus_setup_ring
 * @dev: xenbus device
 * @vaddr: pointer to starting virtual address of the ring
 * @nr_pages: number of pages to be granted
 * @grefs: grant reference array to be filled in
 *
 * Allocate physically contiguous pages for a shared ring buffer and grant it
 * to the peer of the given device. The ring buffer is initially filled with
 * zeroes. The virtual address of the ring is stored at @vaddr and the
 * grant references are stored in the @grefs array. In case of error @vaddr
 * will be set to NULL and @grefs will be filled with INVALID_GRANT_REF.
 */
int xenbus_setup_ring(struct xenbus_device *dev, gfp_t gfp, void **vaddr,
		      unsigned int nr_pages, grant_ref_t *grefs)
{
	unsigned long ring_size = nr_pages * XEN_PAGE_SIZE;
	unsigned int i;
	int ret;

	*vaddr = alloc_pages_exact(ring_size, gfp | __GFP_ZERO);
	if (!*vaddr) {
		ret = -ENOMEM;
		goto err;
	}

	ret = xenbus_grant_ring(dev, *vaddr, nr_pages, grefs);
	if (ret)
		goto err;

	return 0;

 err:
	if (*vaddr)
		free_pages_exact(*vaddr, ring_size);
	for (i = 0; i < nr_pages; i++)
		grefs[i] = INVALID_GRANT_REF;
	*vaddr = NULL;

	return ret;
}
EXPORT_SYMBOL_GPL(xenbus_setup_ring);

/*
 * xenbus_teardown_ring
 * @vaddr: starting virtual address of the ring
 * @nr_pages: number of pages
 * @grefs: grant reference array
 *
 * Remove grants for the shared ring buffer and free the associated memory.
 * On return the grant reference array is filled with INVALID_GRANT_REF.
 */
void xenbus_teardown_ring(void **vaddr, unsigned int nr_pages,
			  grant_ref_t *grefs)
{
	unsigned int i;

	for (i = 0; i < nr_pages; i++) {
		if (grefs[i] != INVALID_GRANT_REF) {
			gnttab_end_foreign_access(grefs[i], 0);
			grefs[i] = INVALID_GRANT_REF;
		}
	}

	if (*vaddr)
		free_pages_exact(*vaddr, nr_pages * XEN_PAGE_SIZE);
	*vaddr = NULL;
}
EXPORT_SYMBOL_GPL(xenbus_teardown_ring);

/**
 * Allocate an event channel for the given xenbus_device, assigning the newly
+4 −0
Original line number Diff line number Diff line
@@ -226,6 +226,10 @@ int xenbus_watch_pathfmt(struct xenbus_device *dev, struct xenbus_watch *watch,
int xenbus_switch_state(struct xenbus_device *dev, enum xenbus_state new_state);
int xenbus_grant_ring(struct xenbus_device *dev, void *vaddr,
		      unsigned int nr_pages, grant_ref_t *grefs);
int xenbus_setup_ring(struct xenbus_device *dev, gfp_t gfp, void **vaddr,
		      unsigned int nr_pages, grant_ref_t *grefs);
void xenbus_teardown_ring(void **vaddr, unsigned int nr_pages,
			  grant_ref_t *grefs);
int xenbus_map_ring_valloc(struct xenbus_device *dev, grant_ref_t *gnt_refs,
			   unsigned int nr_grefs, void **vaddr);