1b3383974SOleksandr Andrushchenko // SPDX-License-Identifier: GPL-2.0 OR MIT 2b3383974SOleksandr Andrushchenko 3b3383974SOleksandr Andrushchenko /* 4b3383974SOleksandr Andrushchenko * Xen frontend/backend page directory based shared buffer 5b3383974SOleksandr Andrushchenko * helper module. 6b3383974SOleksandr Andrushchenko * 7b3383974SOleksandr Andrushchenko * Copyright (C) 2018 EPAM Systems Inc. 8b3383974SOleksandr Andrushchenko * 9b3383974SOleksandr Andrushchenko * Author: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com> 10b3383974SOleksandr Andrushchenko */ 11b3383974SOleksandr Andrushchenko 12b3383974SOleksandr Andrushchenko #include <linux/module.h> 13b3383974SOleksandr Andrushchenko #include <linux/errno.h> 14b3383974SOleksandr Andrushchenko #include <linux/mm.h> 15b3383974SOleksandr Andrushchenko 16b3383974SOleksandr Andrushchenko #include <asm/xen/hypervisor.h> 17b3383974SOleksandr Andrushchenko #include <xen/balloon.h> 18b3383974SOleksandr Andrushchenko #include <xen/xen.h> 19b3383974SOleksandr Andrushchenko #include <xen/xenbus.h> 20b3383974SOleksandr Andrushchenko #include <xen/interface/io/ring.h> 21b3383974SOleksandr Andrushchenko 22b3383974SOleksandr Andrushchenko #include <xen/xen-front-pgdir-shbuf.h> 23b3383974SOleksandr Andrushchenko 24b3383974SOleksandr Andrushchenko /** 25b3383974SOleksandr Andrushchenko * This structure represents the structure of a shared page 26b3383974SOleksandr Andrushchenko * that contains grant references to the pages of the shared 27b3383974SOleksandr Andrushchenko * buffer. This structure is common to many Xen para-virtualized 28b3383974SOleksandr Andrushchenko * protocols at include/xen/interface/io/ 29b3383974SOleksandr Andrushchenko */ 30b3383974SOleksandr Andrushchenko struct xen_page_directory { 31b3383974SOleksandr Andrushchenko grant_ref_t gref_dir_next_page; 32888fd787SJuergen Gross #define XEN_GREF_LIST_END 0 33b3383974SOleksandr Andrushchenko grant_ref_t gref[1]; /* Variable length */ 34b3383974SOleksandr Andrushchenko }; 35b3383974SOleksandr Andrushchenko 36b3383974SOleksandr Andrushchenko /** 37b3383974SOleksandr Andrushchenko * Shared buffer ops which are differently implemented 38b3383974SOleksandr Andrushchenko * depending on the allocation mode, e.g. if the buffer 39b3383974SOleksandr Andrushchenko * is allocated by the corresponding backend or frontend. 40b3383974SOleksandr Andrushchenko * Some of the operations. 41b3383974SOleksandr Andrushchenko */ 42b3383974SOleksandr Andrushchenko struct xen_front_pgdir_shbuf_ops { 43b3383974SOleksandr Andrushchenko /* 44b3383974SOleksandr Andrushchenko * Calculate number of grefs required to handle this buffer, 45b3383974SOleksandr Andrushchenko * e.g. if grefs are required for page directory only or the buffer 46b3383974SOleksandr Andrushchenko * pages as well. 47b3383974SOleksandr Andrushchenko */ 48b3383974SOleksandr Andrushchenko void (*calc_num_grefs)(struct xen_front_pgdir_shbuf *buf); 49b3383974SOleksandr Andrushchenko 50b3383974SOleksandr Andrushchenko /* Fill page directory according to para-virtual display protocol. */ 51b3383974SOleksandr Andrushchenko void (*fill_page_dir)(struct xen_front_pgdir_shbuf *buf); 52b3383974SOleksandr Andrushchenko 53b3383974SOleksandr Andrushchenko /* Claim grant references for the pages of the buffer. */ 54b3383974SOleksandr Andrushchenko int (*grant_refs_for_buffer)(struct xen_front_pgdir_shbuf *buf, 55b3383974SOleksandr Andrushchenko grant_ref_t *priv_gref_head, int gref_idx); 56b3383974SOleksandr Andrushchenko 57b3383974SOleksandr Andrushchenko /* Map grant references of the buffer. */ 58b3383974SOleksandr Andrushchenko int (*map)(struct xen_front_pgdir_shbuf *buf); 59b3383974SOleksandr Andrushchenko 60b3383974SOleksandr Andrushchenko /* Unmap grant references of the buffer. */ 61b3383974SOleksandr Andrushchenko int (*unmap)(struct xen_front_pgdir_shbuf *buf); 62b3383974SOleksandr Andrushchenko }; 63b3383974SOleksandr Andrushchenko 64b3383974SOleksandr Andrushchenko /** 65b3383974SOleksandr Andrushchenko * Get granted reference to the very first page of the 66b3383974SOleksandr Andrushchenko * page directory. Usually this is passed to the backend, 67b3383974SOleksandr Andrushchenko * so it can find/fill the grant references to the buffer's 68b3383974SOleksandr Andrushchenko * pages. 69b3383974SOleksandr Andrushchenko * 70b3383974SOleksandr Andrushchenko * \param buf shared buffer which page directory is of interest. 71b3383974SOleksandr Andrushchenko * \return granted reference to the very first page of the 72b3383974SOleksandr Andrushchenko * page directory. 73b3383974SOleksandr Andrushchenko */ 74b3383974SOleksandr Andrushchenko grant_ref_t 75b3383974SOleksandr Andrushchenko xen_front_pgdir_shbuf_get_dir_start(struct xen_front_pgdir_shbuf *buf) 76b3383974SOleksandr Andrushchenko { 77b3383974SOleksandr Andrushchenko if (!buf->grefs) 78888fd787SJuergen Gross return INVALID_GRANT_REF; 79b3383974SOleksandr Andrushchenko 80b3383974SOleksandr Andrushchenko return buf->grefs[0]; 81b3383974SOleksandr Andrushchenko } 82b3383974SOleksandr Andrushchenko EXPORT_SYMBOL_GPL(xen_front_pgdir_shbuf_get_dir_start); 83b3383974SOleksandr Andrushchenko 84b3383974SOleksandr Andrushchenko /** 85b3383974SOleksandr Andrushchenko * Map granted references of the shared buffer. 86b3383974SOleksandr Andrushchenko * 87b3383974SOleksandr Andrushchenko * Depending on the shared buffer mode of allocation 88b3383974SOleksandr Andrushchenko * (be_alloc flag) this can either do nothing (for buffers 89b3383974SOleksandr Andrushchenko * shared by the frontend itself) or map the provided granted 90b3383974SOleksandr Andrushchenko * references onto the backing storage (buf->pages). 91b3383974SOleksandr Andrushchenko * 92*8441dac0SZhang Jiaming * \param buf shared buffer which grants to be mapped. 93b3383974SOleksandr Andrushchenko * \return zero on success or a negative number on failure. 94b3383974SOleksandr Andrushchenko */ 95b3383974SOleksandr Andrushchenko int xen_front_pgdir_shbuf_map(struct xen_front_pgdir_shbuf *buf) 96b3383974SOleksandr Andrushchenko { 97b3383974SOleksandr Andrushchenko if (buf->ops && buf->ops->map) 98b3383974SOleksandr Andrushchenko return buf->ops->map(buf); 99b3383974SOleksandr Andrushchenko 100b3383974SOleksandr Andrushchenko /* No need to map own grant references. */ 101b3383974SOleksandr Andrushchenko return 0; 102b3383974SOleksandr Andrushchenko } 103b3383974SOleksandr Andrushchenko EXPORT_SYMBOL_GPL(xen_front_pgdir_shbuf_map); 104b3383974SOleksandr Andrushchenko 105b3383974SOleksandr Andrushchenko /** 106b3383974SOleksandr Andrushchenko * Unmap granted references of the shared buffer. 107b3383974SOleksandr Andrushchenko * 108b3383974SOleksandr Andrushchenko * Depending on the shared buffer mode of allocation 109b3383974SOleksandr Andrushchenko * (be_alloc flag) this can either do nothing (for buffers 110b3383974SOleksandr Andrushchenko * shared by the frontend itself) or unmap the provided granted 111b3383974SOleksandr Andrushchenko * references. 112b3383974SOleksandr Andrushchenko * 113*8441dac0SZhang Jiaming * \param buf shared buffer which grants to be unmapped. 114b3383974SOleksandr Andrushchenko * \return zero on success or a negative number on failure. 115b3383974SOleksandr Andrushchenko */ 116b3383974SOleksandr Andrushchenko int xen_front_pgdir_shbuf_unmap(struct xen_front_pgdir_shbuf *buf) 117b3383974SOleksandr Andrushchenko { 118b3383974SOleksandr Andrushchenko if (buf->ops && buf->ops->unmap) 119b3383974SOleksandr Andrushchenko return buf->ops->unmap(buf); 120b3383974SOleksandr Andrushchenko 121b3383974SOleksandr Andrushchenko /* No need to unmap own grant references. */ 122b3383974SOleksandr Andrushchenko return 0; 123b3383974SOleksandr Andrushchenko } 124b3383974SOleksandr Andrushchenko EXPORT_SYMBOL_GPL(xen_front_pgdir_shbuf_unmap); 125b3383974SOleksandr Andrushchenko 126b3383974SOleksandr Andrushchenko /** 127b3383974SOleksandr Andrushchenko * Free all the resources of the shared buffer. 128b3383974SOleksandr Andrushchenko * 129b3383974SOleksandr Andrushchenko * \param buf shared buffer which resources to be freed. 130b3383974SOleksandr Andrushchenko */ 131b3383974SOleksandr Andrushchenko void xen_front_pgdir_shbuf_free(struct xen_front_pgdir_shbuf *buf) 132b3383974SOleksandr Andrushchenko { 133b3383974SOleksandr Andrushchenko if (buf->grefs) { 134b3383974SOleksandr Andrushchenko int i; 135b3383974SOleksandr Andrushchenko 136b3383974SOleksandr Andrushchenko for (i = 0; i < buf->num_grefs; i++) 137888fd787SJuergen Gross if (buf->grefs[i] != INVALID_GRANT_REF) 13849f8b459SJuergen Gross gnttab_end_foreign_access(buf->grefs[i], NULL); 139b3383974SOleksandr Andrushchenko } 140b3383974SOleksandr Andrushchenko kfree(buf->grefs); 141b3383974SOleksandr Andrushchenko kfree(buf->directory); 142b3383974SOleksandr Andrushchenko } 143b3383974SOleksandr Andrushchenko EXPORT_SYMBOL_GPL(xen_front_pgdir_shbuf_free); 144b3383974SOleksandr Andrushchenko 145b3383974SOleksandr Andrushchenko /* 146b3383974SOleksandr Andrushchenko * Number of grefs a page can hold with respect to the 147b3383974SOleksandr Andrushchenko * struct xen_page_directory header. 148b3383974SOleksandr Andrushchenko */ 149b3383974SOleksandr Andrushchenko #define XEN_NUM_GREFS_PER_PAGE ((PAGE_SIZE - \ 150b3383974SOleksandr Andrushchenko offsetof(struct xen_page_directory, \ 151b3383974SOleksandr Andrushchenko gref)) / sizeof(grant_ref_t)) 152b3383974SOleksandr Andrushchenko 153b3383974SOleksandr Andrushchenko /** 154b3383974SOleksandr Andrushchenko * Get the number of pages the page directory consumes itself. 155b3383974SOleksandr Andrushchenko * 156b3383974SOleksandr Andrushchenko * \param buf shared buffer. 157b3383974SOleksandr Andrushchenko */ 158b3383974SOleksandr Andrushchenko static int get_num_pages_dir(struct xen_front_pgdir_shbuf *buf) 159b3383974SOleksandr Andrushchenko { 160b3383974SOleksandr Andrushchenko return DIV_ROUND_UP(buf->num_pages, XEN_NUM_GREFS_PER_PAGE); 161b3383974SOleksandr Andrushchenko } 162b3383974SOleksandr Andrushchenko 163b3383974SOleksandr Andrushchenko /** 164b3383974SOleksandr Andrushchenko * Calculate the number of grant references needed to share the buffer 165b3383974SOleksandr Andrushchenko * and its pages when backend allocates the buffer. 166b3383974SOleksandr Andrushchenko * 167b3383974SOleksandr Andrushchenko * \param buf shared buffer. 168b3383974SOleksandr Andrushchenko */ 169b3383974SOleksandr Andrushchenko static void backend_calc_num_grefs(struct xen_front_pgdir_shbuf *buf) 170b3383974SOleksandr Andrushchenko { 171b3383974SOleksandr Andrushchenko /* Only for pages the page directory consumes itself. */ 172b3383974SOleksandr Andrushchenko buf->num_grefs = get_num_pages_dir(buf); 173b3383974SOleksandr Andrushchenko } 174b3383974SOleksandr Andrushchenko 175b3383974SOleksandr Andrushchenko /** 176b3383974SOleksandr Andrushchenko * Calculate the number of grant references needed to share the buffer 177b3383974SOleksandr Andrushchenko * and its pages when frontend allocates the buffer. 178b3383974SOleksandr Andrushchenko * 179b3383974SOleksandr Andrushchenko * \param buf shared buffer. 180b3383974SOleksandr Andrushchenko */ 181b3383974SOleksandr Andrushchenko static void guest_calc_num_grefs(struct xen_front_pgdir_shbuf *buf) 182b3383974SOleksandr Andrushchenko { 183b3383974SOleksandr Andrushchenko /* 184b3383974SOleksandr Andrushchenko * Number of pages the page directory consumes itself 185b3383974SOleksandr Andrushchenko * plus grefs for the buffer pages. 186b3383974SOleksandr Andrushchenko */ 187b3383974SOleksandr Andrushchenko buf->num_grefs = get_num_pages_dir(buf) + buf->num_pages; 188b3383974SOleksandr Andrushchenko } 189b3383974SOleksandr Andrushchenko 190b3383974SOleksandr Andrushchenko #define xen_page_to_vaddr(page) \ 191b3383974SOleksandr Andrushchenko ((uintptr_t)pfn_to_kaddr(page_to_xen_pfn(page))) 192b3383974SOleksandr Andrushchenko 193b3383974SOleksandr Andrushchenko /** 194b3383974SOleksandr Andrushchenko * Unmap the buffer previously mapped with grant references 195b3383974SOleksandr Andrushchenko * provided by the backend. 196b3383974SOleksandr Andrushchenko * 197b3383974SOleksandr Andrushchenko * \param buf shared buffer. 198b3383974SOleksandr Andrushchenko * \return zero on success or a negative number on failure. 199b3383974SOleksandr Andrushchenko */ 200b3383974SOleksandr Andrushchenko static int backend_unmap(struct xen_front_pgdir_shbuf *buf) 201b3383974SOleksandr Andrushchenko { 202b3383974SOleksandr Andrushchenko struct gnttab_unmap_grant_ref *unmap_ops; 203b3383974SOleksandr Andrushchenko int i, ret; 204b3383974SOleksandr Andrushchenko 205b3383974SOleksandr Andrushchenko if (!buf->pages || !buf->backend_map_handles || !buf->grefs) 206b3383974SOleksandr Andrushchenko return 0; 207b3383974SOleksandr Andrushchenko 208b3383974SOleksandr Andrushchenko unmap_ops = kcalloc(buf->num_pages, sizeof(*unmap_ops), 209b3383974SOleksandr Andrushchenko GFP_KERNEL); 210b3383974SOleksandr Andrushchenko if (!unmap_ops) 211b3383974SOleksandr Andrushchenko return -ENOMEM; 212b3383974SOleksandr Andrushchenko 213b3383974SOleksandr Andrushchenko for (i = 0; i < buf->num_pages; i++) { 214b3383974SOleksandr Andrushchenko phys_addr_t addr; 215b3383974SOleksandr Andrushchenko 216b3383974SOleksandr Andrushchenko addr = xen_page_to_vaddr(buf->pages[i]); 217b3383974SOleksandr Andrushchenko gnttab_set_unmap_op(&unmap_ops[i], addr, GNTMAP_host_map, 218b3383974SOleksandr Andrushchenko buf->backend_map_handles[i]); 219b3383974SOleksandr Andrushchenko } 220b3383974SOleksandr Andrushchenko 221b3383974SOleksandr Andrushchenko ret = gnttab_unmap_refs(unmap_ops, NULL, buf->pages, 222b3383974SOleksandr Andrushchenko buf->num_pages); 223b3383974SOleksandr Andrushchenko 224b3383974SOleksandr Andrushchenko for (i = 0; i < buf->num_pages; i++) { 225b3383974SOleksandr Andrushchenko if (unlikely(unmap_ops[i].status != GNTST_okay)) 226b3383974SOleksandr Andrushchenko dev_err(&buf->xb_dev->dev, 227b3383974SOleksandr Andrushchenko "Failed to unmap page %d: %d\n", 228b3383974SOleksandr Andrushchenko i, unmap_ops[i].status); 229b3383974SOleksandr Andrushchenko } 230b3383974SOleksandr Andrushchenko 231b3383974SOleksandr Andrushchenko if (ret) 232b3383974SOleksandr Andrushchenko dev_err(&buf->xb_dev->dev, 233b3383974SOleksandr Andrushchenko "Failed to unmap grant references, ret %d", ret); 234b3383974SOleksandr Andrushchenko 235b3383974SOleksandr Andrushchenko kfree(unmap_ops); 236b3383974SOleksandr Andrushchenko kfree(buf->backend_map_handles); 237b3383974SOleksandr Andrushchenko buf->backend_map_handles = NULL; 238b3383974SOleksandr Andrushchenko return ret; 239b3383974SOleksandr Andrushchenko } 240b3383974SOleksandr Andrushchenko 241b3383974SOleksandr Andrushchenko /** 242b3383974SOleksandr Andrushchenko * Map the buffer with grant references provided by the backend. 243b3383974SOleksandr Andrushchenko * 244b3383974SOleksandr Andrushchenko * \param buf shared buffer. 245b3383974SOleksandr Andrushchenko * \return zero on success or a negative number on failure. 246b3383974SOleksandr Andrushchenko */ 247b3383974SOleksandr Andrushchenko static int backend_map(struct xen_front_pgdir_shbuf *buf) 248b3383974SOleksandr Andrushchenko { 249b3383974SOleksandr Andrushchenko struct gnttab_map_grant_ref *map_ops = NULL; 250b3383974SOleksandr Andrushchenko unsigned char *ptr; 251b3383974SOleksandr Andrushchenko int ret, cur_gref, cur_dir_page, cur_page, grefs_left; 252b3383974SOleksandr Andrushchenko 253b3383974SOleksandr Andrushchenko map_ops = kcalloc(buf->num_pages, sizeof(*map_ops), GFP_KERNEL); 254b3383974SOleksandr Andrushchenko if (!map_ops) 255b3383974SOleksandr Andrushchenko return -ENOMEM; 256b3383974SOleksandr Andrushchenko 257b3383974SOleksandr Andrushchenko buf->backend_map_handles = kcalloc(buf->num_pages, 258b3383974SOleksandr Andrushchenko sizeof(*buf->backend_map_handles), 259b3383974SOleksandr Andrushchenko GFP_KERNEL); 260b3383974SOleksandr Andrushchenko if (!buf->backend_map_handles) { 261b3383974SOleksandr Andrushchenko kfree(map_ops); 262b3383974SOleksandr Andrushchenko return -ENOMEM; 263b3383974SOleksandr Andrushchenko } 264b3383974SOleksandr Andrushchenko 265b3383974SOleksandr Andrushchenko /* 266b3383974SOleksandr Andrushchenko * Read page directory to get grefs from the backend: for external 267b3383974SOleksandr Andrushchenko * buffer we only allocate buf->grefs for the page directory, 268b3383974SOleksandr Andrushchenko * so buf->num_grefs has number of pages in the page directory itself. 269b3383974SOleksandr Andrushchenko */ 270b3383974SOleksandr Andrushchenko ptr = buf->directory; 271b3383974SOleksandr Andrushchenko grefs_left = buf->num_pages; 272b3383974SOleksandr Andrushchenko cur_page = 0; 273b3383974SOleksandr Andrushchenko for (cur_dir_page = 0; cur_dir_page < buf->num_grefs; cur_dir_page++) { 274b3383974SOleksandr Andrushchenko struct xen_page_directory *page_dir = 275b3383974SOleksandr Andrushchenko (struct xen_page_directory *)ptr; 276b3383974SOleksandr Andrushchenko int to_copy = XEN_NUM_GREFS_PER_PAGE; 277b3383974SOleksandr Andrushchenko 278b3383974SOleksandr Andrushchenko if (to_copy > grefs_left) 279b3383974SOleksandr Andrushchenko to_copy = grefs_left; 280b3383974SOleksandr Andrushchenko 281b3383974SOleksandr Andrushchenko for (cur_gref = 0; cur_gref < to_copy; cur_gref++) { 282b3383974SOleksandr Andrushchenko phys_addr_t addr; 283b3383974SOleksandr Andrushchenko 284b3383974SOleksandr Andrushchenko addr = xen_page_to_vaddr(buf->pages[cur_page]); 285b3383974SOleksandr Andrushchenko gnttab_set_map_op(&map_ops[cur_page], addr, 286b3383974SOleksandr Andrushchenko GNTMAP_host_map, 287b3383974SOleksandr Andrushchenko page_dir->gref[cur_gref], 288b3383974SOleksandr Andrushchenko buf->xb_dev->otherend_id); 289b3383974SOleksandr Andrushchenko cur_page++; 290b3383974SOleksandr Andrushchenko } 291b3383974SOleksandr Andrushchenko 292b3383974SOleksandr Andrushchenko grefs_left -= to_copy; 293b3383974SOleksandr Andrushchenko ptr += PAGE_SIZE; 294b3383974SOleksandr Andrushchenko } 295b3383974SOleksandr Andrushchenko ret = gnttab_map_refs(map_ops, NULL, buf->pages, buf->num_pages); 296b3383974SOleksandr Andrushchenko 297b3383974SOleksandr Andrushchenko /* Save handles even if error, so we can unmap. */ 298b3383974SOleksandr Andrushchenko for (cur_page = 0; cur_page < buf->num_pages; cur_page++) { 29953f131c2SJan Beulich if (likely(map_ops[cur_page].status == GNTST_okay)) { 30053f131c2SJan Beulich buf->backend_map_handles[cur_page] = 30153f131c2SJan Beulich map_ops[cur_page].handle; 30253f131c2SJan Beulich } else { 30353f131c2SJan Beulich buf->backend_map_handles[cur_page] = 30453f131c2SJan Beulich INVALID_GRANT_HANDLE; 30553f131c2SJan Beulich if (!ret) 30653f131c2SJan Beulich ret = -ENXIO; 307b3383974SOleksandr Andrushchenko dev_err(&buf->xb_dev->dev, 308b3383974SOleksandr Andrushchenko "Failed to map page %d: %d\n", 309b3383974SOleksandr Andrushchenko cur_page, map_ops[cur_page].status); 310b3383974SOleksandr Andrushchenko } 31153f131c2SJan Beulich } 312b3383974SOleksandr Andrushchenko 313b3383974SOleksandr Andrushchenko if (ret) { 314b3383974SOleksandr Andrushchenko dev_err(&buf->xb_dev->dev, 315b3383974SOleksandr Andrushchenko "Failed to map grant references, ret %d", ret); 316b3383974SOleksandr Andrushchenko backend_unmap(buf); 317b3383974SOleksandr Andrushchenko } 318b3383974SOleksandr Andrushchenko 319b3383974SOleksandr Andrushchenko kfree(map_ops); 320b3383974SOleksandr Andrushchenko return ret; 321b3383974SOleksandr Andrushchenko } 322b3383974SOleksandr Andrushchenko 323b3383974SOleksandr Andrushchenko /** 324b3383974SOleksandr Andrushchenko * Fill page directory with grant references to the pages of the 325b3383974SOleksandr Andrushchenko * page directory itself. 326b3383974SOleksandr Andrushchenko * 327b3383974SOleksandr Andrushchenko * The grant references to the buffer pages are provided by the 328b3383974SOleksandr Andrushchenko * backend in this case. 329b3383974SOleksandr Andrushchenko * 330b3383974SOleksandr Andrushchenko * \param buf shared buffer. 331b3383974SOleksandr Andrushchenko */ 332b3383974SOleksandr Andrushchenko static void backend_fill_page_dir(struct xen_front_pgdir_shbuf *buf) 333b3383974SOleksandr Andrushchenko { 334b3383974SOleksandr Andrushchenko struct xen_page_directory *page_dir; 335b3383974SOleksandr Andrushchenko unsigned char *ptr; 336b3383974SOleksandr Andrushchenko int i, num_pages_dir; 337b3383974SOleksandr Andrushchenko 338b3383974SOleksandr Andrushchenko ptr = buf->directory; 339b3383974SOleksandr Andrushchenko num_pages_dir = get_num_pages_dir(buf); 340b3383974SOleksandr Andrushchenko 341b3383974SOleksandr Andrushchenko /* Fill only grefs for the page directory itself. */ 342b3383974SOleksandr Andrushchenko for (i = 0; i < num_pages_dir - 1; i++) { 343b3383974SOleksandr Andrushchenko page_dir = (struct xen_page_directory *)ptr; 344b3383974SOleksandr Andrushchenko 345b3383974SOleksandr Andrushchenko page_dir->gref_dir_next_page = buf->grefs[i + 1]; 346b3383974SOleksandr Andrushchenko ptr += PAGE_SIZE; 347b3383974SOleksandr Andrushchenko } 348b3383974SOleksandr Andrushchenko /* Last page must say there is no more pages. */ 349b3383974SOleksandr Andrushchenko page_dir = (struct xen_page_directory *)ptr; 350888fd787SJuergen Gross page_dir->gref_dir_next_page = XEN_GREF_LIST_END; 351b3383974SOleksandr Andrushchenko } 352b3383974SOleksandr Andrushchenko 353b3383974SOleksandr Andrushchenko /** 354b3383974SOleksandr Andrushchenko * Fill page directory with grant references to the pages of the 355b3383974SOleksandr Andrushchenko * page directory and the buffer we share with the backend. 356b3383974SOleksandr Andrushchenko * 357b3383974SOleksandr Andrushchenko * \param buf shared buffer. 358b3383974SOleksandr Andrushchenko */ 359b3383974SOleksandr Andrushchenko static void guest_fill_page_dir(struct xen_front_pgdir_shbuf *buf) 360b3383974SOleksandr Andrushchenko { 361b3383974SOleksandr Andrushchenko unsigned char *ptr; 362b3383974SOleksandr Andrushchenko int cur_gref, grefs_left, to_copy, i, num_pages_dir; 363b3383974SOleksandr Andrushchenko 364b3383974SOleksandr Andrushchenko ptr = buf->directory; 365b3383974SOleksandr Andrushchenko num_pages_dir = get_num_pages_dir(buf); 366b3383974SOleksandr Andrushchenko 367b3383974SOleksandr Andrushchenko /* 368b3383974SOleksandr Andrushchenko * While copying, skip grefs at start, they are for pages 369b3383974SOleksandr Andrushchenko * granted for the page directory itself. 370b3383974SOleksandr Andrushchenko */ 371b3383974SOleksandr Andrushchenko cur_gref = num_pages_dir; 372b3383974SOleksandr Andrushchenko grefs_left = buf->num_pages; 373b3383974SOleksandr Andrushchenko for (i = 0; i < num_pages_dir; i++) { 374b3383974SOleksandr Andrushchenko struct xen_page_directory *page_dir = 375b3383974SOleksandr Andrushchenko (struct xen_page_directory *)ptr; 376b3383974SOleksandr Andrushchenko 377b3383974SOleksandr Andrushchenko if (grefs_left <= XEN_NUM_GREFS_PER_PAGE) { 378b3383974SOleksandr Andrushchenko to_copy = grefs_left; 379888fd787SJuergen Gross page_dir->gref_dir_next_page = XEN_GREF_LIST_END; 380b3383974SOleksandr Andrushchenko } else { 381b3383974SOleksandr Andrushchenko to_copy = XEN_NUM_GREFS_PER_PAGE; 382b3383974SOleksandr Andrushchenko page_dir->gref_dir_next_page = buf->grefs[i + 1]; 383b3383974SOleksandr Andrushchenko } 384b3383974SOleksandr Andrushchenko memcpy(&page_dir->gref, &buf->grefs[cur_gref], 385b3383974SOleksandr Andrushchenko to_copy * sizeof(grant_ref_t)); 386b3383974SOleksandr Andrushchenko ptr += PAGE_SIZE; 387b3383974SOleksandr Andrushchenko grefs_left -= to_copy; 388b3383974SOleksandr Andrushchenko cur_gref += to_copy; 389b3383974SOleksandr Andrushchenko } 390b3383974SOleksandr Andrushchenko } 391b3383974SOleksandr Andrushchenko 392b3383974SOleksandr Andrushchenko /** 393b3383974SOleksandr Andrushchenko * Grant references to the frontend's buffer pages. 394b3383974SOleksandr Andrushchenko * 395b3383974SOleksandr Andrushchenko * These will be shared with the backend, so it can 396b3383974SOleksandr Andrushchenko * access the buffer's data. 397b3383974SOleksandr Andrushchenko * 398b3383974SOleksandr Andrushchenko * \param buf shared buffer. 399b3383974SOleksandr Andrushchenko * \return zero on success or a negative number on failure. 400b3383974SOleksandr Andrushchenko */ 401b3383974SOleksandr Andrushchenko static int guest_grant_refs_for_buffer(struct xen_front_pgdir_shbuf *buf, 402b3383974SOleksandr Andrushchenko grant_ref_t *priv_gref_head, 403b3383974SOleksandr Andrushchenko int gref_idx) 404b3383974SOleksandr Andrushchenko { 405b3383974SOleksandr Andrushchenko int i, cur_ref, otherend_id; 406b3383974SOleksandr Andrushchenko 407b3383974SOleksandr Andrushchenko otherend_id = buf->xb_dev->otherend_id; 408b3383974SOleksandr Andrushchenko for (i = 0; i < buf->num_pages; i++) { 409b3383974SOleksandr Andrushchenko cur_ref = gnttab_claim_grant_reference(priv_gref_head); 410b3383974SOleksandr Andrushchenko if (cur_ref < 0) 411b3383974SOleksandr Andrushchenko return cur_ref; 412b3383974SOleksandr Andrushchenko 413b3383974SOleksandr Andrushchenko gnttab_grant_foreign_access_ref(cur_ref, otherend_id, 414b3383974SOleksandr Andrushchenko xen_page_to_gfn(buf->pages[i]), 415b3383974SOleksandr Andrushchenko 0); 416b3383974SOleksandr Andrushchenko buf->grefs[gref_idx++] = cur_ref; 417b3383974SOleksandr Andrushchenko } 418b3383974SOleksandr Andrushchenko return 0; 419b3383974SOleksandr Andrushchenko } 420b3383974SOleksandr Andrushchenko 421b3383974SOleksandr Andrushchenko /** 422b3383974SOleksandr Andrushchenko * Grant all the references needed to share the buffer. 423b3383974SOleksandr Andrushchenko * 424b3383974SOleksandr Andrushchenko * Grant references to the page directory pages and, if 425b3383974SOleksandr Andrushchenko * needed, also to the pages of the shared buffer data. 426b3383974SOleksandr Andrushchenko * 427b3383974SOleksandr Andrushchenko * \param buf shared buffer. 428b3383974SOleksandr Andrushchenko * \return zero on success or a negative number on failure. 429b3383974SOleksandr Andrushchenko */ 430b3383974SOleksandr Andrushchenko static int grant_references(struct xen_front_pgdir_shbuf *buf) 431b3383974SOleksandr Andrushchenko { 432b3383974SOleksandr Andrushchenko grant_ref_t priv_gref_head; 433b3383974SOleksandr Andrushchenko int ret, i, j, cur_ref; 434b3383974SOleksandr Andrushchenko int otherend_id, num_pages_dir; 435b3383974SOleksandr Andrushchenko 436b3383974SOleksandr Andrushchenko ret = gnttab_alloc_grant_references(buf->num_grefs, &priv_gref_head); 437b3383974SOleksandr Andrushchenko if (ret < 0) { 438b3383974SOleksandr Andrushchenko dev_err(&buf->xb_dev->dev, 439b3383974SOleksandr Andrushchenko "Cannot allocate grant references\n"); 440b3383974SOleksandr Andrushchenko return ret; 441b3383974SOleksandr Andrushchenko } 442b3383974SOleksandr Andrushchenko 443b3383974SOleksandr Andrushchenko otherend_id = buf->xb_dev->otherend_id; 444b3383974SOleksandr Andrushchenko j = 0; 445b3383974SOleksandr Andrushchenko num_pages_dir = get_num_pages_dir(buf); 446b3383974SOleksandr Andrushchenko for (i = 0; i < num_pages_dir; i++) { 447b3383974SOleksandr Andrushchenko unsigned long frame; 448b3383974SOleksandr Andrushchenko 449b3383974SOleksandr Andrushchenko cur_ref = gnttab_claim_grant_reference(&priv_gref_head); 450b3383974SOleksandr Andrushchenko if (cur_ref < 0) 451b3383974SOleksandr Andrushchenko return cur_ref; 452b3383974SOleksandr Andrushchenko 453b3383974SOleksandr Andrushchenko frame = xen_page_to_gfn(virt_to_page(buf->directory + 454b3383974SOleksandr Andrushchenko PAGE_SIZE * i)); 455b3383974SOleksandr Andrushchenko gnttab_grant_foreign_access_ref(cur_ref, otherend_id, frame, 0); 456b3383974SOleksandr Andrushchenko buf->grefs[j++] = cur_ref; 457b3383974SOleksandr Andrushchenko } 458b3383974SOleksandr Andrushchenko 459b3383974SOleksandr Andrushchenko if (buf->ops->grant_refs_for_buffer) { 460b3383974SOleksandr Andrushchenko ret = buf->ops->grant_refs_for_buffer(buf, &priv_gref_head, j); 461b3383974SOleksandr Andrushchenko if (ret) 462b3383974SOleksandr Andrushchenko return ret; 463b3383974SOleksandr Andrushchenko } 464b3383974SOleksandr Andrushchenko 465b3383974SOleksandr Andrushchenko gnttab_free_grant_references(priv_gref_head); 466b3383974SOleksandr Andrushchenko return 0; 467b3383974SOleksandr Andrushchenko } 468b3383974SOleksandr Andrushchenko 469b3383974SOleksandr Andrushchenko /** 470b3383974SOleksandr Andrushchenko * Allocate all required structures to mange shared buffer. 471b3383974SOleksandr Andrushchenko * 472b3383974SOleksandr Andrushchenko * \param buf shared buffer. 473b3383974SOleksandr Andrushchenko * \return zero on success or a negative number on failure. 474b3383974SOleksandr Andrushchenko */ 475b3383974SOleksandr Andrushchenko static int alloc_storage(struct xen_front_pgdir_shbuf *buf) 476b3383974SOleksandr Andrushchenko { 477b3383974SOleksandr Andrushchenko buf->grefs = kcalloc(buf->num_grefs, sizeof(*buf->grefs), GFP_KERNEL); 478b3383974SOleksandr Andrushchenko if (!buf->grefs) 479b3383974SOleksandr Andrushchenko return -ENOMEM; 480b3383974SOleksandr Andrushchenko 481b3383974SOleksandr Andrushchenko buf->directory = kcalloc(get_num_pages_dir(buf), PAGE_SIZE, GFP_KERNEL); 482b3383974SOleksandr Andrushchenko if (!buf->directory) 483b3383974SOleksandr Andrushchenko return -ENOMEM; 484b3383974SOleksandr Andrushchenko 485b3383974SOleksandr Andrushchenko return 0; 486b3383974SOleksandr Andrushchenko } 487b3383974SOleksandr Andrushchenko 488b3383974SOleksandr Andrushchenko /* 489b3383974SOleksandr Andrushchenko * For backend allocated buffers we don't need grant_refs_for_buffer 490b3383974SOleksandr Andrushchenko * as those grant references are allocated at backend side. 491b3383974SOleksandr Andrushchenko */ 492b3383974SOleksandr Andrushchenko static const struct xen_front_pgdir_shbuf_ops backend_ops = { 493b3383974SOleksandr Andrushchenko .calc_num_grefs = backend_calc_num_grefs, 494b3383974SOleksandr Andrushchenko .fill_page_dir = backend_fill_page_dir, 495b3383974SOleksandr Andrushchenko .map = backend_map, 496b3383974SOleksandr Andrushchenko .unmap = backend_unmap 497b3383974SOleksandr Andrushchenko }; 498b3383974SOleksandr Andrushchenko 499b3383974SOleksandr Andrushchenko /* 500b3383974SOleksandr Andrushchenko * For locally granted references we do not need to map/unmap 501b3383974SOleksandr Andrushchenko * the references. 502b3383974SOleksandr Andrushchenko */ 503b3383974SOleksandr Andrushchenko static const struct xen_front_pgdir_shbuf_ops local_ops = { 504b3383974SOleksandr Andrushchenko .calc_num_grefs = guest_calc_num_grefs, 505b3383974SOleksandr Andrushchenko .fill_page_dir = guest_fill_page_dir, 506b3383974SOleksandr Andrushchenko .grant_refs_for_buffer = guest_grant_refs_for_buffer, 507b3383974SOleksandr Andrushchenko }; 508b3383974SOleksandr Andrushchenko 509b3383974SOleksandr Andrushchenko /** 510b3383974SOleksandr Andrushchenko * Allocate a new instance of a shared buffer. 511b3383974SOleksandr Andrushchenko * 512b3383974SOleksandr Andrushchenko * \param cfg configuration to be used while allocating a new shared buffer. 513b3383974SOleksandr Andrushchenko * \return zero on success or a negative number on failure. 514b3383974SOleksandr Andrushchenko */ 515b3383974SOleksandr Andrushchenko int xen_front_pgdir_shbuf_alloc(struct xen_front_pgdir_shbuf_cfg *cfg) 516b3383974SOleksandr Andrushchenko { 517b3383974SOleksandr Andrushchenko struct xen_front_pgdir_shbuf *buf = cfg->pgdir; 518b3383974SOleksandr Andrushchenko int ret; 519b3383974SOleksandr Andrushchenko 520b3383974SOleksandr Andrushchenko if (cfg->be_alloc) 521b3383974SOleksandr Andrushchenko buf->ops = &backend_ops; 522b3383974SOleksandr Andrushchenko else 523b3383974SOleksandr Andrushchenko buf->ops = &local_ops; 524b3383974SOleksandr Andrushchenko buf->xb_dev = cfg->xb_dev; 525b3383974SOleksandr Andrushchenko buf->num_pages = cfg->num_pages; 526b3383974SOleksandr Andrushchenko buf->pages = cfg->pages; 527b3383974SOleksandr Andrushchenko 528b3383974SOleksandr Andrushchenko buf->ops->calc_num_grefs(buf); 529b3383974SOleksandr Andrushchenko 530b3383974SOleksandr Andrushchenko ret = alloc_storage(buf); 531b3383974SOleksandr Andrushchenko if (ret) 532b3383974SOleksandr Andrushchenko goto fail; 533b3383974SOleksandr Andrushchenko 534b3383974SOleksandr Andrushchenko ret = grant_references(buf); 535b3383974SOleksandr Andrushchenko if (ret) 536b3383974SOleksandr Andrushchenko goto fail; 537b3383974SOleksandr Andrushchenko 538b3383974SOleksandr Andrushchenko buf->ops->fill_page_dir(buf); 539b3383974SOleksandr Andrushchenko 540b3383974SOleksandr Andrushchenko return 0; 541b3383974SOleksandr Andrushchenko 542b3383974SOleksandr Andrushchenko fail: 543b3383974SOleksandr Andrushchenko xen_front_pgdir_shbuf_free(buf); 544b3383974SOleksandr Andrushchenko return ret; 545b3383974SOleksandr Andrushchenko } 546b3383974SOleksandr Andrushchenko EXPORT_SYMBOL_GPL(xen_front_pgdir_shbuf_alloc); 547b3383974SOleksandr Andrushchenko 548b3383974SOleksandr Andrushchenko MODULE_DESCRIPTION("Xen frontend/backend page directory based " 549b3383974SOleksandr Andrushchenko "shared buffer handling"); 550b3383974SOleksandr Andrushchenko MODULE_AUTHOR("Oleksandr Andrushchenko"); 551b3383974SOleksandr Andrushchenko MODULE_LICENSE("GPL"); 552