162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR MIT
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci/*
462306a36Sopenharmony_ci * Xen frontend/backend page directory based shared buffer
562306a36Sopenharmony_ci * helper module.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Copyright (C) 2018 EPAM Systems Inc.
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * Author: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/errno.h>
1462306a36Sopenharmony_ci#include <linux/mm.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include <asm/xen/hypervisor.h>
1762306a36Sopenharmony_ci#include <xen/balloon.h>
1862306a36Sopenharmony_ci#include <xen/xen.h>
1962306a36Sopenharmony_ci#include <xen/xenbus.h>
2062306a36Sopenharmony_ci#include <xen/interface/io/ring.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include <xen/xen-front-pgdir-shbuf.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci/**
2562306a36Sopenharmony_ci * This structure represents the structure of a shared page
2662306a36Sopenharmony_ci * that contains grant references to the pages of the shared
2762306a36Sopenharmony_ci * buffer. This structure is common to many Xen para-virtualized
2862306a36Sopenharmony_ci * protocols at include/xen/interface/io/
2962306a36Sopenharmony_ci */
3062306a36Sopenharmony_cistruct xen_page_directory {
3162306a36Sopenharmony_ci	grant_ref_t gref_dir_next_page;
3262306a36Sopenharmony_ci#define XEN_GREF_LIST_END	0
3362306a36Sopenharmony_ci	grant_ref_t gref[]; /* Variable length */
3462306a36Sopenharmony_ci};
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci/**
3762306a36Sopenharmony_ci * Shared buffer ops which are differently implemented
3862306a36Sopenharmony_ci * depending on the allocation mode, e.g. if the buffer
3962306a36Sopenharmony_ci * is allocated by the corresponding backend or frontend.
4062306a36Sopenharmony_ci * Some of the operations.
4162306a36Sopenharmony_ci */
4262306a36Sopenharmony_cistruct xen_front_pgdir_shbuf_ops {
4362306a36Sopenharmony_ci	/*
4462306a36Sopenharmony_ci	 * Calculate number of grefs required to handle this buffer,
4562306a36Sopenharmony_ci	 * e.g. if grefs are required for page directory only or the buffer
4662306a36Sopenharmony_ci	 * pages as well.
4762306a36Sopenharmony_ci	 */
4862306a36Sopenharmony_ci	void (*calc_num_grefs)(struct xen_front_pgdir_shbuf *buf);
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	/* Fill page directory according to para-virtual display protocol. */
5162306a36Sopenharmony_ci	void (*fill_page_dir)(struct xen_front_pgdir_shbuf *buf);
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	/* Claim grant references for the pages of the buffer. */
5462306a36Sopenharmony_ci	int (*grant_refs_for_buffer)(struct xen_front_pgdir_shbuf *buf,
5562306a36Sopenharmony_ci				     grant_ref_t *priv_gref_head, int gref_idx);
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	/* Map grant references of the buffer. */
5862306a36Sopenharmony_ci	int (*map)(struct xen_front_pgdir_shbuf *buf);
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	/* Unmap grant references of the buffer. */
6162306a36Sopenharmony_ci	int (*unmap)(struct xen_front_pgdir_shbuf *buf);
6262306a36Sopenharmony_ci};
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci/**
6562306a36Sopenharmony_ci * Get granted reference to the very first page of the
6662306a36Sopenharmony_ci * page directory. Usually this is passed to the backend,
6762306a36Sopenharmony_ci * so it can find/fill the grant references to the buffer's
6862306a36Sopenharmony_ci * pages.
6962306a36Sopenharmony_ci *
7062306a36Sopenharmony_ci * \param buf shared buffer which page directory is of interest.
7162306a36Sopenharmony_ci * \return granted reference to the very first page of the
7262306a36Sopenharmony_ci * page directory.
7362306a36Sopenharmony_ci */
7462306a36Sopenharmony_cigrant_ref_t
7562306a36Sopenharmony_cixen_front_pgdir_shbuf_get_dir_start(struct xen_front_pgdir_shbuf *buf)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	if (!buf->grefs)
7862306a36Sopenharmony_ci		return INVALID_GRANT_REF;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	return buf->grefs[0];
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xen_front_pgdir_shbuf_get_dir_start);
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci/**
8562306a36Sopenharmony_ci * Map granted references of the shared buffer.
8662306a36Sopenharmony_ci *
8762306a36Sopenharmony_ci * Depending on the shared buffer mode of allocation
8862306a36Sopenharmony_ci * (be_alloc flag) this can either do nothing (for buffers
8962306a36Sopenharmony_ci * shared by the frontend itself) or map the provided granted
9062306a36Sopenharmony_ci * references onto the backing storage (buf->pages).
9162306a36Sopenharmony_ci *
9262306a36Sopenharmony_ci * \param buf shared buffer which grants to be mapped.
9362306a36Sopenharmony_ci * \return zero on success or a negative number on failure.
9462306a36Sopenharmony_ci */
9562306a36Sopenharmony_ciint xen_front_pgdir_shbuf_map(struct xen_front_pgdir_shbuf *buf)
9662306a36Sopenharmony_ci{
9762306a36Sopenharmony_ci	if (buf->ops && buf->ops->map)
9862306a36Sopenharmony_ci		return buf->ops->map(buf);
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	/* No need to map own grant references. */
10162306a36Sopenharmony_ci	return 0;
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xen_front_pgdir_shbuf_map);
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci/**
10662306a36Sopenharmony_ci * Unmap granted references of the shared buffer.
10762306a36Sopenharmony_ci *
10862306a36Sopenharmony_ci * Depending on the shared buffer mode of allocation
10962306a36Sopenharmony_ci * (be_alloc flag) this can either do nothing (for buffers
11062306a36Sopenharmony_ci * shared by the frontend itself) or unmap the provided granted
11162306a36Sopenharmony_ci * references.
11262306a36Sopenharmony_ci *
11362306a36Sopenharmony_ci * \param buf shared buffer which grants to be unmapped.
11462306a36Sopenharmony_ci * \return zero on success or a negative number on failure.
11562306a36Sopenharmony_ci */
11662306a36Sopenharmony_ciint xen_front_pgdir_shbuf_unmap(struct xen_front_pgdir_shbuf *buf)
11762306a36Sopenharmony_ci{
11862306a36Sopenharmony_ci	if (buf->ops && buf->ops->unmap)
11962306a36Sopenharmony_ci		return buf->ops->unmap(buf);
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	/* No need to unmap own grant references. */
12262306a36Sopenharmony_ci	return 0;
12362306a36Sopenharmony_ci}
12462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xen_front_pgdir_shbuf_unmap);
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci/**
12762306a36Sopenharmony_ci * Free all the resources of the shared buffer.
12862306a36Sopenharmony_ci *
12962306a36Sopenharmony_ci * \param buf shared buffer which resources to be freed.
13062306a36Sopenharmony_ci */
13162306a36Sopenharmony_civoid xen_front_pgdir_shbuf_free(struct xen_front_pgdir_shbuf *buf)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	if (buf->grefs) {
13462306a36Sopenharmony_ci		int i;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci		for (i = 0; i < buf->num_grefs; i++)
13762306a36Sopenharmony_ci			if (buf->grefs[i] != INVALID_GRANT_REF)
13862306a36Sopenharmony_ci				gnttab_end_foreign_access(buf->grefs[i], NULL);
13962306a36Sopenharmony_ci	}
14062306a36Sopenharmony_ci	kfree(buf->grefs);
14162306a36Sopenharmony_ci	kfree(buf->directory);
14262306a36Sopenharmony_ci}
14362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xen_front_pgdir_shbuf_free);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci/*
14662306a36Sopenharmony_ci * Number of grefs a page can hold with respect to the
14762306a36Sopenharmony_ci * struct xen_page_directory header.
14862306a36Sopenharmony_ci */
14962306a36Sopenharmony_ci#define XEN_NUM_GREFS_PER_PAGE ((PAGE_SIZE - \
15062306a36Sopenharmony_ci				 offsetof(struct xen_page_directory, \
15162306a36Sopenharmony_ci					  gref)) / sizeof(grant_ref_t))
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci/**
15462306a36Sopenharmony_ci * Get the number of pages the page directory consumes itself.
15562306a36Sopenharmony_ci *
15662306a36Sopenharmony_ci * \param buf shared buffer.
15762306a36Sopenharmony_ci */
15862306a36Sopenharmony_cistatic int get_num_pages_dir(struct xen_front_pgdir_shbuf *buf)
15962306a36Sopenharmony_ci{
16062306a36Sopenharmony_ci	return DIV_ROUND_UP(buf->num_pages, XEN_NUM_GREFS_PER_PAGE);
16162306a36Sopenharmony_ci}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci/**
16462306a36Sopenharmony_ci * Calculate the number of grant references needed to share the buffer
16562306a36Sopenharmony_ci * and its pages when backend allocates the buffer.
16662306a36Sopenharmony_ci *
16762306a36Sopenharmony_ci * \param buf shared buffer.
16862306a36Sopenharmony_ci */
16962306a36Sopenharmony_cistatic void backend_calc_num_grefs(struct xen_front_pgdir_shbuf *buf)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	/* Only for pages the page directory consumes itself. */
17262306a36Sopenharmony_ci	buf->num_grefs = get_num_pages_dir(buf);
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci/**
17662306a36Sopenharmony_ci * Calculate the number of grant references needed to share the buffer
17762306a36Sopenharmony_ci * and its pages when frontend allocates the buffer.
17862306a36Sopenharmony_ci *
17962306a36Sopenharmony_ci * \param buf shared buffer.
18062306a36Sopenharmony_ci */
18162306a36Sopenharmony_cistatic void guest_calc_num_grefs(struct xen_front_pgdir_shbuf *buf)
18262306a36Sopenharmony_ci{
18362306a36Sopenharmony_ci	/*
18462306a36Sopenharmony_ci	 * Number of pages the page directory consumes itself
18562306a36Sopenharmony_ci	 * plus grefs for the buffer pages.
18662306a36Sopenharmony_ci	 */
18762306a36Sopenharmony_ci	buf->num_grefs = get_num_pages_dir(buf) + buf->num_pages;
18862306a36Sopenharmony_ci}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci#define xen_page_to_vaddr(page) \
19162306a36Sopenharmony_ci	((uintptr_t)pfn_to_kaddr(page_to_xen_pfn(page)))
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci/**
19462306a36Sopenharmony_ci * Unmap the buffer previously mapped with grant references
19562306a36Sopenharmony_ci * provided by the backend.
19662306a36Sopenharmony_ci *
19762306a36Sopenharmony_ci * \param buf shared buffer.
19862306a36Sopenharmony_ci * \return zero on success or a negative number on failure.
19962306a36Sopenharmony_ci */
20062306a36Sopenharmony_cistatic int backend_unmap(struct xen_front_pgdir_shbuf *buf)
20162306a36Sopenharmony_ci{
20262306a36Sopenharmony_ci	struct gnttab_unmap_grant_ref *unmap_ops;
20362306a36Sopenharmony_ci	int i, ret;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	if (!buf->pages || !buf->backend_map_handles || !buf->grefs)
20662306a36Sopenharmony_ci		return 0;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	unmap_ops = kcalloc(buf->num_pages, sizeof(*unmap_ops),
20962306a36Sopenharmony_ci			    GFP_KERNEL);
21062306a36Sopenharmony_ci	if (!unmap_ops)
21162306a36Sopenharmony_ci		return -ENOMEM;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	for (i = 0; i < buf->num_pages; i++) {
21462306a36Sopenharmony_ci		phys_addr_t addr;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci		addr = xen_page_to_vaddr(buf->pages[i]);
21762306a36Sopenharmony_ci		gnttab_set_unmap_op(&unmap_ops[i], addr, GNTMAP_host_map,
21862306a36Sopenharmony_ci				    buf->backend_map_handles[i]);
21962306a36Sopenharmony_ci	}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	ret = gnttab_unmap_refs(unmap_ops, NULL, buf->pages,
22262306a36Sopenharmony_ci				buf->num_pages);
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	for (i = 0; i < buf->num_pages; i++) {
22562306a36Sopenharmony_ci		if (unlikely(unmap_ops[i].status != GNTST_okay))
22662306a36Sopenharmony_ci			dev_err(&buf->xb_dev->dev,
22762306a36Sopenharmony_ci				"Failed to unmap page %d: %d\n",
22862306a36Sopenharmony_ci				i, unmap_ops[i].status);
22962306a36Sopenharmony_ci	}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	if (ret)
23262306a36Sopenharmony_ci		dev_err(&buf->xb_dev->dev,
23362306a36Sopenharmony_ci			"Failed to unmap grant references, ret %d", ret);
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	kfree(unmap_ops);
23662306a36Sopenharmony_ci	kfree(buf->backend_map_handles);
23762306a36Sopenharmony_ci	buf->backend_map_handles = NULL;
23862306a36Sopenharmony_ci	return ret;
23962306a36Sopenharmony_ci}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci/**
24262306a36Sopenharmony_ci * Map the buffer with grant references provided by the backend.
24362306a36Sopenharmony_ci *
24462306a36Sopenharmony_ci * \param buf shared buffer.
24562306a36Sopenharmony_ci * \return zero on success or a negative number on failure.
24662306a36Sopenharmony_ci */
24762306a36Sopenharmony_cistatic int backend_map(struct xen_front_pgdir_shbuf *buf)
24862306a36Sopenharmony_ci{
24962306a36Sopenharmony_ci	struct gnttab_map_grant_ref *map_ops = NULL;
25062306a36Sopenharmony_ci	unsigned char *ptr;
25162306a36Sopenharmony_ci	int ret, cur_gref, cur_dir_page, cur_page, grefs_left;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	map_ops = kcalloc(buf->num_pages, sizeof(*map_ops), GFP_KERNEL);
25462306a36Sopenharmony_ci	if (!map_ops)
25562306a36Sopenharmony_ci		return -ENOMEM;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	buf->backend_map_handles = kcalloc(buf->num_pages,
25862306a36Sopenharmony_ci					   sizeof(*buf->backend_map_handles),
25962306a36Sopenharmony_ci					   GFP_KERNEL);
26062306a36Sopenharmony_ci	if (!buf->backend_map_handles) {
26162306a36Sopenharmony_ci		kfree(map_ops);
26262306a36Sopenharmony_ci		return -ENOMEM;
26362306a36Sopenharmony_ci	}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	/*
26662306a36Sopenharmony_ci	 * Read page directory to get grefs from the backend: for external
26762306a36Sopenharmony_ci	 * buffer we only allocate buf->grefs for the page directory,
26862306a36Sopenharmony_ci	 * so buf->num_grefs has number of pages in the page directory itself.
26962306a36Sopenharmony_ci	 */
27062306a36Sopenharmony_ci	ptr = buf->directory;
27162306a36Sopenharmony_ci	grefs_left = buf->num_pages;
27262306a36Sopenharmony_ci	cur_page = 0;
27362306a36Sopenharmony_ci	for (cur_dir_page = 0; cur_dir_page < buf->num_grefs; cur_dir_page++) {
27462306a36Sopenharmony_ci		struct xen_page_directory *page_dir =
27562306a36Sopenharmony_ci			(struct xen_page_directory *)ptr;
27662306a36Sopenharmony_ci		int to_copy = XEN_NUM_GREFS_PER_PAGE;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci		if (to_copy > grefs_left)
27962306a36Sopenharmony_ci			to_copy = grefs_left;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci		for (cur_gref = 0; cur_gref < to_copy; cur_gref++) {
28262306a36Sopenharmony_ci			phys_addr_t addr;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci			addr = xen_page_to_vaddr(buf->pages[cur_page]);
28562306a36Sopenharmony_ci			gnttab_set_map_op(&map_ops[cur_page], addr,
28662306a36Sopenharmony_ci					  GNTMAP_host_map,
28762306a36Sopenharmony_ci					  page_dir->gref[cur_gref],
28862306a36Sopenharmony_ci					  buf->xb_dev->otherend_id);
28962306a36Sopenharmony_ci			cur_page++;
29062306a36Sopenharmony_ci		}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci		grefs_left -= to_copy;
29362306a36Sopenharmony_ci		ptr += PAGE_SIZE;
29462306a36Sopenharmony_ci	}
29562306a36Sopenharmony_ci	ret = gnttab_map_refs(map_ops, NULL, buf->pages, buf->num_pages);
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	/* Save handles even if error, so we can unmap. */
29862306a36Sopenharmony_ci	for (cur_page = 0; cur_page < buf->num_pages; cur_page++) {
29962306a36Sopenharmony_ci		if (likely(map_ops[cur_page].status == GNTST_okay)) {
30062306a36Sopenharmony_ci			buf->backend_map_handles[cur_page] =
30162306a36Sopenharmony_ci				map_ops[cur_page].handle;
30262306a36Sopenharmony_ci		} else {
30362306a36Sopenharmony_ci			buf->backend_map_handles[cur_page] =
30462306a36Sopenharmony_ci				INVALID_GRANT_HANDLE;
30562306a36Sopenharmony_ci			if (!ret)
30662306a36Sopenharmony_ci				ret = -ENXIO;
30762306a36Sopenharmony_ci			dev_err(&buf->xb_dev->dev,
30862306a36Sopenharmony_ci				"Failed to map page %d: %d\n",
30962306a36Sopenharmony_ci				cur_page, map_ops[cur_page].status);
31062306a36Sopenharmony_ci		}
31162306a36Sopenharmony_ci	}
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	if (ret) {
31462306a36Sopenharmony_ci		dev_err(&buf->xb_dev->dev,
31562306a36Sopenharmony_ci			"Failed to map grant references, ret %d", ret);
31662306a36Sopenharmony_ci		backend_unmap(buf);
31762306a36Sopenharmony_ci	}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	kfree(map_ops);
32062306a36Sopenharmony_ci	return ret;
32162306a36Sopenharmony_ci}
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci/**
32462306a36Sopenharmony_ci * Fill page directory with grant references to the pages of the
32562306a36Sopenharmony_ci * page directory itself.
32662306a36Sopenharmony_ci *
32762306a36Sopenharmony_ci * The grant references to the buffer pages are provided by the
32862306a36Sopenharmony_ci * backend in this case.
32962306a36Sopenharmony_ci *
33062306a36Sopenharmony_ci * \param buf shared buffer.
33162306a36Sopenharmony_ci */
33262306a36Sopenharmony_cistatic void backend_fill_page_dir(struct xen_front_pgdir_shbuf *buf)
33362306a36Sopenharmony_ci{
33462306a36Sopenharmony_ci	struct xen_page_directory *page_dir;
33562306a36Sopenharmony_ci	unsigned char *ptr;
33662306a36Sopenharmony_ci	int i, num_pages_dir;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	ptr = buf->directory;
33962306a36Sopenharmony_ci	num_pages_dir = get_num_pages_dir(buf);
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	/* Fill only grefs for the page directory itself. */
34262306a36Sopenharmony_ci	for (i = 0; i < num_pages_dir - 1; i++) {
34362306a36Sopenharmony_ci		page_dir = (struct xen_page_directory *)ptr;
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci		page_dir->gref_dir_next_page = buf->grefs[i + 1];
34662306a36Sopenharmony_ci		ptr += PAGE_SIZE;
34762306a36Sopenharmony_ci	}
34862306a36Sopenharmony_ci	/* Last page must say there is no more pages. */
34962306a36Sopenharmony_ci	page_dir = (struct xen_page_directory *)ptr;
35062306a36Sopenharmony_ci	page_dir->gref_dir_next_page = XEN_GREF_LIST_END;
35162306a36Sopenharmony_ci}
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci/**
35462306a36Sopenharmony_ci * Fill page directory with grant references to the pages of the
35562306a36Sopenharmony_ci * page directory and the buffer we share with the backend.
35662306a36Sopenharmony_ci *
35762306a36Sopenharmony_ci * \param buf shared buffer.
35862306a36Sopenharmony_ci */
35962306a36Sopenharmony_cistatic void guest_fill_page_dir(struct xen_front_pgdir_shbuf *buf)
36062306a36Sopenharmony_ci{
36162306a36Sopenharmony_ci	unsigned char *ptr;
36262306a36Sopenharmony_ci	int cur_gref, grefs_left, to_copy, i, num_pages_dir;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	ptr = buf->directory;
36562306a36Sopenharmony_ci	num_pages_dir = get_num_pages_dir(buf);
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	/*
36862306a36Sopenharmony_ci	 * While copying, skip grefs at start, they are for pages
36962306a36Sopenharmony_ci	 * granted for the page directory itself.
37062306a36Sopenharmony_ci	 */
37162306a36Sopenharmony_ci	cur_gref = num_pages_dir;
37262306a36Sopenharmony_ci	grefs_left = buf->num_pages;
37362306a36Sopenharmony_ci	for (i = 0; i < num_pages_dir; i++) {
37462306a36Sopenharmony_ci		struct xen_page_directory *page_dir =
37562306a36Sopenharmony_ci			(struct xen_page_directory *)ptr;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci		if (grefs_left <= XEN_NUM_GREFS_PER_PAGE) {
37862306a36Sopenharmony_ci			to_copy = grefs_left;
37962306a36Sopenharmony_ci			page_dir->gref_dir_next_page = XEN_GREF_LIST_END;
38062306a36Sopenharmony_ci		} else {
38162306a36Sopenharmony_ci			to_copy = XEN_NUM_GREFS_PER_PAGE;
38262306a36Sopenharmony_ci			page_dir->gref_dir_next_page = buf->grefs[i + 1];
38362306a36Sopenharmony_ci		}
38462306a36Sopenharmony_ci		memcpy(&page_dir->gref, &buf->grefs[cur_gref],
38562306a36Sopenharmony_ci		       to_copy * sizeof(grant_ref_t));
38662306a36Sopenharmony_ci		ptr += PAGE_SIZE;
38762306a36Sopenharmony_ci		grefs_left -= to_copy;
38862306a36Sopenharmony_ci		cur_gref += to_copy;
38962306a36Sopenharmony_ci	}
39062306a36Sopenharmony_ci}
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci/**
39362306a36Sopenharmony_ci * Grant references to the frontend's buffer pages.
39462306a36Sopenharmony_ci *
39562306a36Sopenharmony_ci * These will be shared with the backend, so it can
39662306a36Sopenharmony_ci * access the buffer's data.
39762306a36Sopenharmony_ci *
39862306a36Sopenharmony_ci * \param buf shared buffer.
39962306a36Sopenharmony_ci * \return zero on success or a negative number on failure.
40062306a36Sopenharmony_ci */
40162306a36Sopenharmony_cistatic int guest_grant_refs_for_buffer(struct xen_front_pgdir_shbuf *buf,
40262306a36Sopenharmony_ci				       grant_ref_t *priv_gref_head,
40362306a36Sopenharmony_ci				       int gref_idx)
40462306a36Sopenharmony_ci{
40562306a36Sopenharmony_ci	int i, cur_ref, otherend_id;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	otherend_id = buf->xb_dev->otherend_id;
40862306a36Sopenharmony_ci	for (i = 0; i < buf->num_pages; i++) {
40962306a36Sopenharmony_ci		cur_ref = gnttab_claim_grant_reference(priv_gref_head);
41062306a36Sopenharmony_ci		if (cur_ref < 0)
41162306a36Sopenharmony_ci			return cur_ref;
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci		gnttab_grant_foreign_access_ref(cur_ref, otherend_id,
41462306a36Sopenharmony_ci						xen_page_to_gfn(buf->pages[i]),
41562306a36Sopenharmony_ci						0);
41662306a36Sopenharmony_ci		buf->grefs[gref_idx++] = cur_ref;
41762306a36Sopenharmony_ci	}
41862306a36Sopenharmony_ci	return 0;
41962306a36Sopenharmony_ci}
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci/**
42262306a36Sopenharmony_ci * Grant all the references needed to share the buffer.
42362306a36Sopenharmony_ci *
42462306a36Sopenharmony_ci * Grant references to the page directory pages and, if
42562306a36Sopenharmony_ci * needed, also to the pages of the shared buffer data.
42662306a36Sopenharmony_ci *
42762306a36Sopenharmony_ci * \param buf shared buffer.
42862306a36Sopenharmony_ci * \return zero on success or a negative number on failure.
42962306a36Sopenharmony_ci */
43062306a36Sopenharmony_cistatic int grant_references(struct xen_front_pgdir_shbuf *buf)
43162306a36Sopenharmony_ci{
43262306a36Sopenharmony_ci	grant_ref_t priv_gref_head;
43362306a36Sopenharmony_ci	int ret, i, j, cur_ref;
43462306a36Sopenharmony_ci	int otherend_id, num_pages_dir;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	ret = gnttab_alloc_grant_references(buf->num_grefs, &priv_gref_head);
43762306a36Sopenharmony_ci	if (ret < 0) {
43862306a36Sopenharmony_ci		dev_err(&buf->xb_dev->dev,
43962306a36Sopenharmony_ci			"Cannot allocate grant references\n");
44062306a36Sopenharmony_ci		return ret;
44162306a36Sopenharmony_ci	}
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	otherend_id = buf->xb_dev->otherend_id;
44462306a36Sopenharmony_ci	j = 0;
44562306a36Sopenharmony_ci	num_pages_dir = get_num_pages_dir(buf);
44662306a36Sopenharmony_ci	for (i = 0; i < num_pages_dir; i++) {
44762306a36Sopenharmony_ci		unsigned long frame;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci		cur_ref = gnttab_claim_grant_reference(&priv_gref_head);
45062306a36Sopenharmony_ci		if (cur_ref < 0)
45162306a36Sopenharmony_ci			return cur_ref;
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci		frame = xen_page_to_gfn(virt_to_page(buf->directory +
45462306a36Sopenharmony_ci						     PAGE_SIZE * i));
45562306a36Sopenharmony_ci		gnttab_grant_foreign_access_ref(cur_ref, otherend_id, frame, 0);
45662306a36Sopenharmony_ci		buf->grefs[j++] = cur_ref;
45762306a36Sopenharmony_ci	}
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	if (buf->ops->grant_refs_for_buffer) {
46062306a36Sopenharmony_ci		ret = buf->ops->grant_refs_for_buffer(buf, &priv_gref_head, j);
46162306a36Sopenharmony_ci		if (ret)
46262306a36Sopenharmony_ci			return ret;
46362306a36Sopenharmony_ci	}
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	gnttab_free_grant_references(priv_gref_head);
46662306a36Sopenharmony_ci	return 0;
46762306a36Sopenharmony_ci}
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci/**
47062306a36Sopenharmony_ci * Allocate all required structures to mange shared buffer.
47162306a36Sopenharmony_ci *
47262306a36Sopenharmony_ci * \param buf shared buffer.
47362306a36Sopenharmony_ci * \return zero on success or a negative number on failure.
47462306a36Sopenharmony_ci */
47562306a36Sopenharmony_cistatic int alloc_storage(struct xen_front_pgdir_shbuf *buf)
47662306a36Sopenharmony_ci{
47762306a36Sopenharmony_ci	buf->grefs = kcalloc(buf->num_grefs, sizeof(*buf->grefs), GFP_KERNEL);
47862306a36Sopenharmony_ci	if (!buf->grefs)
47962306a36Sopenharmony_ci		return -ENOMEM;
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	buf->directory = kcalloc(get_num_pages_dir(buf), PAGE_SIZE, GFP_KERNEL);
48262306a36Sopenharmony_ci	if (!buf->directory)
48362306a36Sopenharmony_ci		return -ENOMEM;
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	return 0;
48662306a36Sopenharmony_ci}
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci/*
48962306a36Sopenharmony_ci * For backend allocated buffers we don't need grant_refs_for_buffer
49062306a36Sopenharmony_ci * as those grant references are allocated at backend side.
49162306a36Sopenharmony_ci */
49262306a36Sopenharmony_cistatic const struct xen_front_pgdir_shbuf_ops backend_ops = {
49362306a36Sopenharmony_ci	.calc_num_grefs = backend_calc_num_grefs,
49462306a36Sopenharmony_ci	.fill_page_dir = backend_fill_page_dir,
49562306a36Sopenharmony_ci	.map = backend_map,
49662306a36Sopenharmony_ci	.unmap = backend_unmap
49762306a36Sopenharmony_ci};
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci/*
50062306a36Sopenharmony_ci * For locally granted references we do not need to map/unmap
50162306a36Sopenharmony_ci * the references.
50262306a36Sopenharmony_ci */
50362306a36Sopenharmony_cistatic const struct xen_front_pgdir_shbuf_ops local_ops = {
50462306a36Sopenharmony_ci	.calc_num_grefs = guest_calc_num_grefs,
50562306a36Sopenharmony_ci	.fill_page_dir = guest_fill_page_dir,
50662306a36Sopenharmony_ci	.grant_refs_for_buffer = guest_grant_refs_for_buffer,
50762306a36Sopenharmony_ci};
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci/**
51062306a36Sopenharmony_ci * Allocate a new instance of a shared buffer.
51162306a36Sopenharmony_ci *
51262306a36Sopenharmony_ci * \param cfg configuration to be used while allocating a new shared buffer.
51362306a36Sopenharmony_ci * \return zero on success or a negative number on failure.
51462306a36Sopenharmony_ci */
51562306a36Sopenharmony_ciint xen_front_pgdir_shbuf_alloc(struct xen_front_pgdir_shbuf_cfg *cfg)
51662306a36Sopenharmony_ci{
51762306a36Sopenharmony_ci	struct xen_front_pgdir_shbuf *buf = cfg->pgdir;
51862306a36Sopenharmony_ci	int ret;
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	if (cfg->be_alloc)
52162306a36Sopenharmony_ci		buf->ops = &backend_ops;
52262306a36Sopenharmony_ci	else
52362306a36Sopenharmony_ci		buf->ops = &local_ops;
52462306a36Sopenharmony_ci	buf->xb_dev = cfg->xb_dev;
52562306a36Sopenharmony_ci	buf->num_pages = cfg->num_pages;
52662306a36Sopenharmony_ci	buf->pages = cfg->pages;
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	buf->ops->calc_num_grefs(buf);
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	ret = alloc_storage(buf);
53162306a36Sopenharmony_ci	if (ret)
53262306a36Sopenharmony_ci		goto fail;
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	ret = grant_references(buf);
53562306a36Sopenharmony_ci	if (ret)
53662306a36Sopenharmony_ci		goto fail;
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	buf->ops->fill_page_dir(buf);
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	return 0;
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_cifail:
54362306a36Sopenharmony_ci	xen_front_pgdir_shbuf_free(buf);
54462306a36Sopenharmony_ci	return ret;
54562306a36Sopenharmony_ci}
54662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xen_front_pgdir_shbuf_alloc);
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ciMODULE_DESCRIPTION("Xen frontend/backend page directory based "
54962306a36Sopenharmony_ci		   "shared buffer handling");
55062306a36Sopenharmony_ciMODULE_AUTHOR("Oleksandr Andrushchenko");
55162306a36Sopenharmony_ciMODULE_LICENSE("GPL");
552