18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR MIT 28c2ecf20Sopenharmony_ci 38c2ecf20Sopenharmony_ci/* 48c2ecf20Sopenharmony_ci * Xen frontend/backend page directory based shared buffer 58c2ecf20Sopenharmony_ci * helper module. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (C) 2018 EPAM Systems Inc. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Author: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com> 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/errno.h> 148c2ecf20Sopenharmony_ci#include <linux/mm.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <asm/xen/hypervisor.h> 178c2ecf20Sopenharmony_ci#include <xen/balloon.h> 188c2ecf20Sopenharmony_ci#include <xen/xen.h> 198c2ecf20Sopenharmony_ci#include <xen/xenbus.h> 208c2ecf20Sopenharmony_ci#include <xen/interface/io/ring.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include <xen/xen-front-pgdir-shbuf.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#ifndef GRANT_INVALID_REF 258c2ecf20Sopenharmony_ci/* 268c2ecf20Sopenharmony_ci * FIXME: usage of grant reference 0 as invalid grant reference: 278c2ecf20Sopenharmony_ci * grant reference 0 is valid, but never exposed to a PV driver, 288c2ecf20Sopenharmony_ci * because of the fact it is already in use/reserved by the PV console. 298c2ecf20Sopenharmony_ci */ 308c2ecf20Sopenharmony_ci#define GRANT_INVALID_REF 0 318c2ecf20Sopenharmony_ci#endif 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci/** 348c2ecf20Sopenharmony_ci * This structure represents the structure of a shared page 358c2ecf20Sopenharmony_ci * that contains grant references to the pages of the shared 368c2ecf20Sopenharmony_ci * buffer. This structure is common to many Xen para-virtualized 378c2ecf20Sopenharmony_ci * protocols at include/xen/interface/io/ 388c2ecf20Sopenharmony_ci */ 398c2ecf20Sopenharmony_cistruct xen_page_directory { 408c2ecf20Sopenharmony_ci grant_ref_t gref_dir_next_page; 418c2ecf20Sopenharmony_ci grant_ref_t gref[1]; /* Variable length */ 428c2ecf20Sopenharmony_ci}; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci/** 458c2ecf20Sopenharmony_ci * Shared buffer ops which are differently implemented 468c2ecf20Sopenharmony_ci * depending on the allocation mode, e.g. if the buffer 478c2ecf20Sopenharmony_ci * is allocated by the corresponding backend or frontend. 488c2ecf20Sopenharmony_ci * Some of the operations. 498c2ecf20Sopenharmony_ci */ 508c2ecf20Sopenharmony_cistruct xen_front_pgdir_shbuf_ops { 518c2ecf20Sopenharmony_ci /* 528c2ecf20Sopenharmony_ci * Calculate number of grefs required to handle this buffer, 538c2ecf20Sopenharmony_ci * e.g. if grefs are required for page directory only or the buffer 548c2ecf20Sopenharmony_ci * pages as well. 558c2ecf20Sopenharmony_ci */ 568c2ecf20Sopenharmony_ci void (*calc_num_grefs)(struct xen_front_pgdir_shbuf *buf); 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci /* Fill page directory according to para-virtual display protocol. */ 598c2ecf20Sopenharmony_ci void (*fill_page_dir)(struct xen_front_pgdir_shbuf *buf); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci /* Claim grant references for the pages of the buffer. */ 628c2ecf20Sopenharmony_ci int (*grant_refs_for_buffer)(struct xen_front_pgdir_shbuf *buf, 638c2ecf20Sopenharmony_ci grant_ref_t *priv_gref_head, int gref_idx); 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci /* Map grant references of the buffer. */ 668c2ecf20Sopenharmony_ci int (*map)(struct xen_front_pgdir_shbuf *buf); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci /* Unmap grant references of the buffer. */ 698c2ecf20Sopenharmony_ci int (*unmap)(struct xen_front_pgdir_shbuf *buf); 708c2ecf20Sopenharmony_ci}; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci/** 738c2ecf20Sopenharmony_ci * Get granted reference to the very first page of the 748c2ecf20Sopenharmony_ci * page directory. Usually this is passed to the backend, 758c2ecf20Sopenharmony_ci * so it can find/fill the grant references to the buffer's 768c2ecf20Sopenharmony_ci * pages. 778c2ecf20Sopenharmony_ci * 788c2ecf20Sopenharmony_ci * \param buf shared buffer which page directory is of interest. 798c2ecf20Sopenharmony_ci * \return granted reference to the very first page of the 808c2ecf20Sopenharmony_ci * page directory. 818c2ecf20Sopenharmony_ci */ 828c2ecf20Sopenharmony_cigrant_ref_t 838c2ecf20Sopenharmony_cixen_front_pgdir_shbuf_get_dir_start(struct xen_front_pgdir_shbuf *buf) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci if (!buf->grefs) 868c2ecf20Sopenharmony_ci return GRANT_INVALID_REF; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci return buf->grefs[0]; 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xen_front_pgdir_shbuf_get_dir_start); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci/** 938c2ecf20Sopenharmony_ci * Map granted references of the shared buffer. 948c2ecf20Sopenharmony_ci * 958c2ecf20Sopenharmony_ci * Depending on the shared buffer mode of allocation 968c2ecf20Sopenharmony_ci * (be_alloc flag) this can either do nothing (for buffers 978c2ecf20Sopenharmony_ci * shared by the frontend itself) or map the provided granted 988c2ecf20Sopenharmony_ci * references onto the backing storage (buf->pages). 998c2ecf20Sopenharmony_ci * 1008c2ecf20Sopenharmony_ci * \param buf shared buffer which grants to be maped. 1018c2ecf20Sopenharmony_ci * \return zero on success or a negative number on failure. 1028c2ecf20Sopenharmony_ci */ 1038c2ecf20Sopenharmony_ciint xen_front_pgdir_shbuf_map(struct xen_front_pgdir_shbuf *buf) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci if (buf->ops && buf->ops->map) 1068c2ecf20Sopenharmony_ci return buf->ops->map(buf); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci /* No need to map own grant references. */ 1098c2ecf20Sopenharmony_ci return 0; 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xen_front_pgdir_shbuf_map); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci/** 1148c2ecf20Sopenharmony_ci * Unmap granted references of the shared buffer. 1158c2ecf20Sopenharmony_ci * 1168c2ecf20Sopenharmony_ci * Depending on the shared buffer mode of allocation 1178c2ecf20Sopenharmony_ci * (be_alloc flag) this can either do nothing (for buffers 1188c2ecf20Sopenharmony_ci * shared by the frontend itself) or unmap the provided granted 1198c2ecf20Sopenharmony_ci * references. 1208c2ecf20Sopenharmony_ci * 1218c2ecf20Sopenharmony_ci * \param buf shared buffer which grants to be unmaped. 1228c2ecf20Sopenharmony_ci * \return zero on success or a negative number on failure. 1238c2ecf20Sopenharmony_ci */ 1248c2ecf20Sopenharmony_ciint xen_front_pgdir_shbuf_unmap(struct xen_front_pgdir_shbuf *buf) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci if (buf->ops && buf->ops->unmap) 1278c2ecf20Sopenharmony_ci return buf->ops->unmap(buf); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci /* No need to unmap own grant references. */ 1308c2ecf20Sopenharmony_ci return 0; 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xen_front_pgdir_shbuf_unmap); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci/** 1358c2ecf20Sopenharmony_ci * Free all the resources of the shared buffer. 1368c2ecf20Sopenharmony_ci * 1378c2ecf20Sopenharmony_ci * \param buf shared buffer which resources to be freed. 1388c2ecf20Sopenharmony_ci */ 1398c2ecf20Sopenharmony_civoid xen_front_pgdir_shbuf_free(struct xen_front_pgdir_shbuf *buf) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci if (buf->grefs) { 1428c2ecf20Sopenharmony_ci int i; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci for (i = 0; i < buf->num_grefs; i++) 1458c2ecf20Sopenharmony_ci if (buf->grefs[i] != GRANT_INVALID_REF) 1468c2ecf20Sopenharmony_ci gnttab_end_foreign_access(buf->grefs[i], 1478c2ecf20Sopenharmony_ci 0, 0UL); 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci kfree(buf->grefs); 1508c2ecf20Sopenharmony_ci kfree(buf->directory); 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xen_front_pgdir_shbuf_free); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci/* 1558c2ecf20Sopenharmony_ci * Number of grefs a page can hold with respect to the 1568c2ecf20Sopenharmony_ci * struct xen_page_directory header. 1578c2ecf20Sopenharmony_ci */ 1588c2ecf20Sopenharmony_ci#define XEN_NUM_GREFS_PER_PAGE ((PAGE_SIZE - \ 1598c2ecf20Sopenharmony_ci offsetof(struct xen_page_directory, \ 1608c2ecf20Sopenharmony_ci gref)) / sizeof(grant_ref_t)) 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci/** 1638c2ecf20Sopenharmony_ci * Get the number of pages the page directory consumes itself. 1648c2ecf20Sopenharmony_ci * 1658c2ecf20Sopenharmony_ci * \param buf shared buffer. 1668c2ecf20Sopenharmony_ci */ 1678c2ecf20Sopenharmony_cistatic int get_num_pages_dir(struct xen_front_pgdir_shbuf *buf) 1688c2ecf20Sopenharmony_ci{ 1698c2ecf20Sopenharmony_ci return DIV_ROUND_UP(buf->num_pages, XEN_NUM_GREFS_PER_PAGE); 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci/** 1738c2ecf20Sopenharmony_ci * Calculate the number of grant references needed to share the buffer 1748c2ecf20Sopenharmony_ci * and its pages when backend allocates the buffer. 1758c2ecf20Sopenharmony_ci * 1768c2ecf20Sopenharmony_ci * \param buf shared buffer. 1778c2ecf20Sopenharmony_ci */ 1788c2ecf20Sopenharmony_cistatic void backend_calc_num_grefs(struct xen_front_pgdir_shbuf *buf) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci /* Only for pages the page directory consumes itself. */ 1818c2ecf20Sopenharmony_ci buf->num_grefs = get_num_pages_dir(buf); 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci/** 1858c2ecf20Sopenharmony_ci * Calculate the number of grant references needed to share the buffer 1868c2ecf20Sopenharmony_ci * and its pages when frontend allocates the buffer. 1878c2ecf20Sopenharmony_ci * 1888c2ecf20Sopenharmony_ci * \param buf shared buffer. 1898c2ecf20Sopenharmony_ci */ 1908c2ecf20Sopenharmony_cistatic void guest_calc_num_grefs(struct xen_front_pgdir_shbuf *buf) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci /* 1938c2ecf20Sopenharmony_ci * Number of pages the page directory consumes itself 1948c2ecf20Sopenharmony_ci * plus grefs for the buffer pages. 1958c2ecf20Sopenharmony_ci */ 1968c2ecf20Sopenharmony_ci buf->num_grefs = get_num_pages_dir(buf) + buf->num_pages; 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci#define xen_page_to_vaddr(page) \ 2008c2ecf20Sopenharmony_ci ((uintptr_t)pfn_to_kaddr(page_to_xen_pfn(page))) 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci/** 2038c2ecf20Sopenharmony_ci * Unmap the buffer previously mapped with grant references 2048c2ecf20Sopenharmony_ci * provided by the backend. 2058c2ecf20Sopenharmony_ci * 2068c2ecf20Sopenharmony_ci * \param buf shared buffer. 2078c2ecf20Sopenharmony_ci * \return zero on success or a negative number on failure. 2088c2ecf20Sopenharmony_ci */ 2098c2ecf20Sopenharmony_cistatic int backend_unmap(struct xen_front_pgdir_shbuf *buf) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci struct gnttab_unmap_grant_ref *unmap_ops; 2128c2ecf20Sopenharmony_ci int i, ret; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci if (!buf->pages || !buf->backend_map_handles || !buf->grefs) 2158c2ecf20Sopenharmony_ci return 0; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci unmap_ops = kcalloc(buf->num_pages, sizeof(*unmap_ops), 2188c2ecf20Sopenharmony_ci GFP_KERNEL); 2198c2ecf20Sopenharmony_ci if (!unmap_ops) 2208c2ecf20Sopenharmony_ci return -ENOMEM; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci for (i = 0; i < buf->num_pages; i++) { 2238c2ecf20Sopenharmony_ci phys_addr_t addr; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci addr = xen_page_to_vaddr(buf->pages[i]); 2268c2ecf20Sopenharmony_ci gnttab_set_unmap_op(&unmap_ops[i], addr, GNTMAP_host_map, 2278c2ecf20Sopenharmony_ci buf->backend_map_handles[i]); 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci ret = gnttab_unmap_refs(unmap_ops, NULL, buf->pages, 2318c2ecf20Sopenharmony_ci buf->num_pages); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci for (i = 0; i < buf->num_pages; i++) { 2348c2ecf20Sopenharmony_ci if (unlikely(unmap_ops[i].status != GNTST_okay)) 2358c2ecf20Sopenharmony_ci dev_err(&buf->xb_dev->dev, 2368c2ecf20Sopenharmony_ci "Failed to unmap page %d: %d\n", 2378c2ecf20Sopenharmony_ci i, unmap_ops[i].status); 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci if (ret) 2418c2ecf20Sopenharmony_ci dev_err(&buf->xb_dev->dev, 2428c2ecf20Sopenharmony_ci "Failed to unmap grant references, ret %d", ret); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci kfree(unmap_ops); 2458c2ecf20Sopenharmony_ci kfree(buf->backend_map_handles); 2468c2ecf20Sopenharmony_ci buf->backend_map_handles = NULL; 2478c2ecf20Sopenharmony_ci return ret; 2488c2ecf20Sopenharmony_ci} 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci/** 2518c2ecf20Sopenharmony_ci * Map the buffer with grant references provided by the backend. 2528c2ecf20Sopenharmony_ci * 2538c2ecf20Sopenharmony_ci * \param buf shared buffer. 2548c2ecf20Sopenharmony_ci * \return zero on success or a negative number on failure. 2558c2ecf20Sopenharmony_ci */ 2568c2ecf20Sopenharmony_cistatic int backend_map(struct xen_front_pgdir_shbuf *buf) 2578c2ecf20Sopenharmony_ci{ 2588c2ecf20Sopenharmony_ci struct gnttab_map_grant_ref *map_ops = NULL; 2598c2ecf20Sopenharmony_ci unsigned char *ptr; 2608c2ecf20Sopenharmony_ci int ret, cur_gref, cur_dir_page, cur_page, grefs_left; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci map_ops = kcalloc(buf->num_pages, sizeof(*map_ops), GFP_KERNEL); 2638c2ecf20Sopenharmony_ci if (!map_ops) 2648c2ecf20Sopenharmony_ci return -ENOMEM; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci buf->backend_map_handles = kcalloc(buf->num_pages, 2678c2ecf20Sopenharmony_ci sizeof(*buf->backend_map_handles), 2688c2ecf20Sopenharmony_ci GFP_KERNEL); 2698c2ecf20Sopenharmony_ci if (!buf->backend_map_handles) { 2708c2ecf20Sopenharmony_ci kfree(map_ops); 2718c2ecf20Sopenharmony_ci return -ENOMEM; 2728c2ecf20Sopenharmony_ci } 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci /* 2758c2ecf20Sopenharmony_ci * Read page directory to get grefs from the backend: for external 2768c2ecf20Sopenharmony_ci * buffer we only allocate buf->grefs for the page directory, 2778c2ecf20Sopenharmony_ci * so buf->num_grefs has number of pages in the page directory itself. 2788c2ecf20Sopenharmony_ci */ 2798c2ecf20Sopenharmony_ci ptr = buf->directory; 2808c2ecf20Sopenharmony_ci grefs_left = buf->num_pages; 2818c2ecf20Sopenharmony_ci cur_page = 0; 2828c2ecf20Sopenharmony_ci for (cur_dir_page = 0; cur_dir_page < buf->num_grefs; cur_dir_page++) { 2838c2ecf20Sopenharmony_ci struct xen_page_directory *page_dir = 2848c2ecf20Sopenharmony_ci (struct xen_page_directory *)ptr; 2858c2ecf20Sopenharmony_ci int to_copy = XEN_NUM_GREFS_PER_PAGE; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci if (to_copy > grefs_left) 2888c2ecf20Sopenharmony_ci to_copy = grefs_left; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci for (cur_gref = 0; cur_gref < to_copy; cur_gref++) { 2918c2ecf20Sopenharmony_ci phys_addr_t addr; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci addr = xen_page_to_vaddr(buf->pages[cur_page]); 2948c2ecf20Sopenharmony_ci gnttab_set_map_op(&map_ops[cur_page], addr, 2958c2ecf20Sopenharmony_ci GNTMAP_host_map, 2968c2ecf20Sopenharmony_ci page_dir->gref[cur_gref], 2978c2ecf20Sopenharmony_ci buf->xb_dev->otherend_id); 2988c2ecf20Sopenharmony_ci cur_page++; 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci grefs_left -= to_copy; 3028c2ecf20Sopenharmony_ci ptr += PAGE_SIZE; 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci ret = gnttab_map_refs(map_ops, NULL, buf->pages, buf->num_pages); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci /* Save handles even if error, so we can unmap. */ 3078c2ecf20Sopenharmony_ci for (cur_page = 0; cur_page < buf->num_pages; cur_page++) { 3088c2ecf20Sopenharmony_ci buf->backend_map_handles[cur_page] = map_ops[cur_page].handle; 3098c2ecf20Sopenharmony_ci if (unlikely(map_ops[cur_page].status != GNTST_okay)) 3108c2ecf20Sopenharmony_ci dev_err(&buf->xb_dev->dev, 3118c2ecf20Sopenharmony_ci "Failed to map page %d: %d\n", 3128c2ecf20Sopenharmony_ci cur_page, map_ops[cur_page].status); 3138c2ecf20Sopenharmony_ci } 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci if (ret) { 3168c2ecf20Sopenharmony_ci dev_err(&buf->xb_dev->dev, 3178c2ecf20Sopenharmony_ci "Failed to map grant references, ret %d", ret); 3188c2ecf20Sopenharmony_ci backend_unmap(buf); 3198c2ecf20Sopenharmony_ci } 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci kfree(map_ops); 3228c2ecf20Sopenharmony_ci return ret; 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci/** 3268c2ecf20Sopenharmony_ci * Fill page directory with grant references to the pages of the 3278c2ecf20Sopenharmony_ci * page directory itself. 3288c2ecf20Sopenharmony_ci * 3298c2ecf20Sopenharmony_ci * The grant references to the buffer pages are provided by the 3308c2ecf20Sopenharmony_ci * backend in this case. 3318c2ecf20Sopenharmony_ci * 3328c2ecf20Sopenharmony_ci * \param buf shared buffer. 3338c2ecf20Sopenharmony_ci */ 3348c2ecf20Sopenharmony_cistatic void backend_fill_page_dir(struct xen_front_pgdir_shbuf *buf) 3358c2ecf20Sopenharmony_ci{ 3368c2ecf20Sopenharmony_ci struct xen_page_directory *page_dir; 3378c2ecf20Sopenharmony_ci unsigned char *ptr; 3388c2ecf20Sopenharmony_ci int i, num_pages_dir; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci ptr = buf->directory; 3418c2ecf20Sopenharmony_ci num_pages_dir = get_num_pages_dir(buf); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci /* Fill only grefs for the page directory itself. */ 3448c2ecf20Sopenharmony_ci for (i = 0; i < num_pages_dir - 1; i++) { 3458c2ecf20Sopenharmony_ci page_dir = (struct xen_page_directory *)ptr; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci page_dir->gref_dir_next_page = buf->grefs[i + 1]; 3488c2ecf20Sopenharmony_ci ptr += PAGE_SIZE; 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci /* Last page must say there is no more pages. */ 3518c2ecf20Sopenharmony_ci page_dir = (struct xen_page_directory *)ptr; 3528c2ecf20Sopenharmony_ci page_dir->gref_dir_next_page = GRANT_INVALID_REF; 3538c2ecf20Sopenharmony_ci} 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci/** 3568c2ecf20Sopenharmony_ci * Fill page directory with grant references to the pages of the 3578c2ecf20Sopenharmony_ci * page directory and the buffer we share with the backend. 3588c2ecf20Sopenharmony_ci * 3598c2ecf20Sopenharmony_ci * \param buf shared buffer. 3608c2ecf20Sopenharmony_ci */ 3618c2ecf20Sopenharmony_cistatic void guest_fill_page_dir(struct xen_front_pgdir_shbuf *buf) 3628c2ecf20Sopenharmony_ci{ 3638c2ecf20Sopenharmony_ci unsigned char *ptr; 3648c2ecf20Sopenharmony_ci int cur_gref, grefs_left, to_copy, i, num_pages_dir; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci ptr = buf->directory; 3678c2ecf20Sopenharmony_ci num_pages_dir = get_num_pages_dir(buf); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci /* 3708c2ecf20Sopenharmony_ci * While copying, skip grefs at start, they are for pages 3718c2ecf20Sopenharmony_ci * granted for the page directory itself. 3728c2ecf20Sopenharmony_ci */ 3738c2ecf20Sopenharmony_ci cur_gref = num_pages_dir; 3748c2ecf20Sopenharmony_ci grefs_left = buf->num_pages; 3758c2ecf20Sopenharmony_ci for (i = 0; i < num_pages_dir; i++) { 3768c2ecf20Sopenharmony_ci struct xen_page_directory *page_dir = 3778c2ecf20Sopenharmony_ci (struct xen_page_directory *)ptr; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci if (grefs_left <= XEN_NUM_GREFS_PER_PAGE) { 3808c2ecf20Sopenharmony_ci to_copy = grefs_left; 3818c2ecf20Sopenharmony_ci page_dir->gref_dir_next_page = GRANT_INVALID_REF; 3828c2ecf20Sopenharmony_ci } else { 3838c2ecf20Sopenharmony_ci to_copy = XEN_NUM_GREFS_PER_PAGE; 3848c2ecf20Sopenharmony_ci page_dir->gref_dir_next_page = buf->grefs[i + 1]; 3858c2ecf20Sopenharmony_ci } 3868c2ecf20Sopenharmony_ci memcpy(&page_dir->gref, &buf->grefs[cur_gref], 3878c2ecf20Sopenharmony_ci to_copy * sizeof(grant_ref_t)); 3888c2ecf20Sopenharmony_ci ptr += PAGE_SIZE; 3898c2ecf20Sopenharmony_ci grefs_left -= to_copy; 3908c2ecf20Sopenharmony_ci cur_gref += to_copy; 3918c2ecf20Sopenharmony_ci } 3928c2ecf20Sopenharmony_ci} 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci/** 3958c2ecf20Sopenharmony_ci * Grant references to the frontend's buffer pages. 3968c2ecf20Sopenharmony_ci * 3978c2ecf20Sopenharmony_ci * These will be shared with the backend, so it can 3988c2ecf20Sopenharmony_ci * access the buffer's data. 3998c2ecf20Sopenharmony_ci * 4008c2ecf20Sopenharmony_ci * \param buf shared buffer. 4018c2ecf20Sopenharmony_ci * \return zero on success or a negative number on failure. 4028c2ecf20Sopenharmony_ci */ 4038c2ecf20Sopenharmony_cistatic int guest_grant_refs_for_buffer(struct xen_front_pgdir_shbuf *buf, 4048c2ecf20Sopenharmony_ci grant_ref_t *priv_gref_head, 4058c2ecf20Sopenharmony_ci int gref_idx) 4068c2ecf20Sopenharmony_ci{ 4078c2ecf20Sopenharmony_ci int i, cur_ref, otherend_id; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci otherend_id = buf->xb_dev->otherend_id; 4108c2ecf20Sopenharmony_ci for (i = 0; i < buf->num_pages; i++) { 4118c2ecf20Sopenharmony_ci cur_ref = gnttab_claim_grant_reference(priv_gref_head); 4128c2ecf20Sopenharmony_ci if (cur_ref < 0) 4138c2ecf20Sopenharmony_ci return cur_ref; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci gnttab_grant_foreign_access_ref(cur_ref, otherend_id, 4168c2ecf20Sopenharmony_ci xen_page_to_gfn(buf->pages[i]), 4178c2ecf20Sopenharmony_ci 0); 4188c2ecf20Sopenharmony_ci buf->grefs[gref_idx++] = cur_ref; 4198c2ecf20Sopenharmony_ci } 4208c2ecf20Sopenharmony_ci return 0; 4218c2ecf20Sopenharmony_ci} 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci/** 4248c2ecf20Sopenharmony_ci * Grant all the references needed to share the buffer. 4258c2ecf20Sopenharmony_ci * 4268c2ecf20Sopenharmony_ci * Grant references to the page directory pages and, if 4278c2ecf20Sopenharmony_ci * needed, also to the pages of the shared buffer data. 4288c2ecf20Sopenharmony_ci * 4298c2ecf20Sopenharmony_ci * \param buf shared buffer. 4308c2ecf20Sopenharmony_ci * \return zero on success or a negative number on failure. 4318c2ecf20Sopenharmony_ci */ 4328c2ecf20Sopenharmony_cistatic int grant_references(struct xen_front_pgdir_shbuf *buf) 4338c2ecf20Sopenharmony_ci{ 4348c2ecf20Sopenharmony_ci grant_ref_t priv_gref_head; 4358c2ecf20Sopenharmony_ci int ret, i, j, cur_ref; 4368c2ecf20Sopenharmony_ci int otherend_id, num_pages_dir; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci ret = gnttab_alloc_grant_references(buf->num_grefs, &priv_gref_head); 4398c2ecf20Sopenharmony_ci if (ret < 0) { 4408c2ecf20Sopenharmony_ci dev_err(&buf->xb_dev->dev, 4418c2ecf20Sopenharmony_ci "Cannot allocate grant references\n"); 4428c2ecf20Sopenharmony_ci return ret; 4438c2ecf20Sopenharmony_ci } 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci otherend_id = buf->xb_dev->otherend_id; 4468c2ecf20Sopenharmony_ci j = 0; 4478c2ecf20Sopenharmony_ci num_pages_dir = get_num_pages_dir(buf); 4488c2ecf20Sopenharmony_ci for (i = 0; i < num_pages_dir; i++) { 4498c2ecf20Sopenharmony_ci unsigned long frame; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci cur_ref = gnttab_claim_grant_reference(&priv_gref_head); 4528c2ecf20Sopenharmony_ci if (cur_ref < 0) 4538c2ecf20Sopenharmony_ci return cur_ref; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci frame = xen_page_to_gfn(virt_to_page(buf->directory + 4568c2ecf20Sopenharmony_ci PAGE_SIZE * i)); 4578c2ecf20Sopenharmony_ci gnttab_grant_foreign_access_ref(cur_ref, otherend_id, frame, 0); 4588c2ecf20Sopenharmony_ci buf->grefs[j++] = cur_ref; 4598c2ecf20Sopenharmony_ci } 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci if (buf->ops->grant_refs_for_buffer) { 4628c2ecf20Sopenharmony_ci ret = buf->ops->grant_refs_for_buffer(buf, &priv_gref_head, j); 4638c2ecf20Sopenharmony_ci if (ret) 4648c2ecf20Sopenharmony_ci return ret; 4658c2ecf20Sopenharmony_ci } 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci gnttab_free_grant_references(priv_gref_head); 4688c2ecf20Sopenharmony_ci return 0; 4698c2ecf20Sopenharmony_ci} 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci/** 4728c2ecf20Sopenharmony_ci * Allocate all required structures to mange shared buffer. 4738c2ecf20Sopenharmony_ci * 4748c2ecf20Sopenharmony_ci * \param buf shared buffer. 4758c2ecf20Sopenharmony_ci * \return zero on success or a negative number on failure. 4768c2ecf20Sopenharmony_ci */ 4778c2ecf20Sopenharmony_cistatic int alloc_storage(struct xen_front_pgdir_shbuf *buf) 4788c2ecf20Sopenharmony_ci{ 4798c2ecf20Sopenharmony_ci buf->grefs = kcalloc(buf->num_grefs, sizeof(*buf->grefs), GFP_KERNEL); 4808c2ecf20Sopenharmony_ci if (!buf->grefs) 4818c2ecf20Sopenharmony_ci return -ENOMEM; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci buf->directory = kcalloc(get_num_pages_dir(buf), PAGE_SIZE, GFP_KERNEL); 4848c2ecf20Sopenharmony_ci if (!buf->directory) 4858c2ecf20Sopenharmony_ci return -ENOMEM; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci return 0; 4888c2ecf20Sopenharmony_ci} 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci/* 4918c2ecf20Sopenharmony_ci * For backend allocated buffers we don't need grant_refs_for_buffer 4928c2ecf20Sopenharmony_ci * as those grant references are allocated at backend side. 4938c2ecf20Sopenharmony_ci */ 4948c2ecf20Sopenharmony_cistatic const struct xen_front_pgdir_shbuf_ops backend_ops = { 4958c2ecf20Sopenharmony_ci .calc_num_grefs = backend_calc_num_grefs, 4968c2ecf20Sopenharmony_ci .fill_page_dir = backend_fill_page_dir, 4978c2ecf20Sopenharmony_ci .map = backend_map, 4988c2ecf20Sopenharmony_ci .unmap = backend_unmap 4998c2ecf20Sopenharmony_ci}; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci/* 5028c2ecf20Sopenharmony_ci * For locally granted references we do not need to map/unmap 5038c2ecf20Sopenharmony_ci * the references. 5048c2ecf20Sopenharmony_ci */ 5058c2ecf20Sopenharmony_cistatic const struct xen_front_pgdir_shbuf_ops local_ops = { 5068c2ecf20Sopenharmony_ci .calc_num_grefs = guest_calc_num_grefs, 5078c2ecf20Sopenharmony_ci .fill_page_dir = guest_fill_page_dir, 5088c2ecf20Sopenharmony_ci .grant_refs_for_buffer = guest_grant_refs_for_buffer, 5098c2ecf20Sopenharmony_ci}; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci/** 5128c2ecf20Sopenharmony_ci * Allocate a new instance of a shared buffer. 5138c2ecf20Sopenharmony_ci * 5148c2ecf20Sopenharmony_ci * \param cfg configuration to be used while allocating a new shared buffer. 5158c2ecf20Sopenharmony_ci * \return zero on success or a negative number on failure. 5168c2ecf20Sopenharmony_ci */ 5178c2ecf20Sopenharmony_ciint xen_front_pgdir_shbuf_alloc(struct xen_front_pgdir_shbuf_cfg *cfg) 5188c2ecf20Sopenharmony_ci{ 5198c2ecf20Sopenharmony_ci struct xen_front_pgdir_shbuf *buf = cfg->pgdir; 5208c2ecf20Sopenharmony_ci int ret; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci if (cfg->be_alloc) 5238c2ecf20Sopenharmony_ci buf->ops = &backend_ops; 5248c2ecf20Sopenharmony_ci else 5258c2ecf20Sopenharmony_ci buf->ops = &local_ops; 5268c2ecf20Sopenharmony_ci buf->xb_dev = cfg->xb_dev; 5278c2ecf20Sopenharmony_ci buf->num_pages = cfg->num_pages; 5288c2ecf20Sopenharmony_ci buf->pages = cfg->pages; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci buf->ops->calc_num_grefs(buf); 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci ret = alloc_storage(buf); 5338c2ecf20Sopenharmony_ci if (ret) 5348c2ecf20Sopenharmony_ci goto fail; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci ret = grant_references(buf); 5378c2ecf20Sopenharmony_ci if (ret) 5388c2ecf20Sopenharmony_ci goto fail; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci buf->ops->fill_page_dir(buf); 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci return 0; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_cifail: 5458c2ecf20Sopenharmony_ci xen_front_pgdir_shbuf_free(buf); 5468c2ecf20Sopenharmony_ci return ret; 5478c2ecf20Sopenharmony_ci} 5488c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xen_front_pgdir_shbuf_alloc); 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Xen frontend/backend page directory based " 5518c2ecf20Sopenharmony_ci "shared buffer handling"); 5528c2ecf20Sopenharmony_ciMODULE_AUTHOR("Oleksandr Andrushchenko"); 5538c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 554