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