Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ALSA: xen-front: Implement handling of shared buffers

Implement shared buffer handling according to the
para-virtualized sound device protocol at xen/interface/io/sndif.h:
- manage buffer memory
- handle granted references
- handle page directories

[ Fixed missing linux/kernel.h inclusion -- tiwai ]

Signed-off-by: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>

authored by

Oleksandr Andrushchenko and committed by
Takashi Iwai
d6e0fbb8 788ef64a

+240 -1
+2 -1
sound/xen/Makefile
··· 2 2 3 3 snd_xen_front-objs := xen_snd_front.o \ 4 4 xen_snd_front_cfg.o \ 5 - xen_snd_front_evtchnl.o 5 + xen_snd_front_evtchnl.o \ 6 + xen_snd_front_shbuf.o 6 7 7 8 obj-$(CONFIG_SND_XEN_FRONTEND) += snd_xen_front.o
+8
sound/xen/xen_snd_front.c
··· 11 11 #include <linux/delay.h> 12 12 #include <linux/module.h> 13 13 14 + #include <xen/page.h> 14 15 #include <xen/platform_pci.h> 15 16 #include <xen/xen.h> 16 17 #include <xen/xenbus.h> ··· 191 190 192 191 if (!xen_has_pv_devices()) 193 192 return -ENODEV; 193 + 194 + /* At the moment we only support case with XEN_PAGE_SIZE == PAGE_SIZE */ 195 + if (XEN_PAGE_SIZE != PAGE_SIZE) { 196 + pr_err(XENSND_DRIVER_NAME ": different kernel and Xen page sizes are not supported: XEN_PAGE_SIZE (%lu) != PAGE_SIZE (%lu)\n", 197 + XEN_PAGE_SIZE, PAGE_SIZE); 198 + return -ENODEV; 199 + } 194 200 195 201 pr_info("Initialising Xen " XENSND_DRIVER_NAME " frontend driver\n"); 196 202 return xenbus_register_frontend(&xen_driver);
+194
sound/xen/xen_snd_front_shbuf.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 OR MIT 2 + 3 + /* 4 + * Xen para-virtual sound device 5 + * 6 + * Copyright (C) 2016-2018 EPAM Systems Inc. 7 + * 8 + * Author: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com> 9 + */ 10 + 11 + #include <linux/kernel.h> 12 + #include <xen/xen.h> 13 + #include <xen/xenbus.h> 14 + 15 + #include "xen_snd_front_shbuf.h" 16 + 17 + grant_ref_t xen_snd_front_shbuf_get_dir_start(struct xen_snd_front_shbuf *buf) 18 + { 19 + if (!buf->grefs) 20 + return GRANT_INVALID_REF; 21 + 22 + return buf->grefs[0]; 23 + } 24 + 25 + void xen_snd_front_shbuf_clear(struct xen_snd_front_shbuf *buf) 26 + { 27 + memset(buf, 0, sizeof(*buf)); 28 + } 29 + 30 + void xen_snd_front_shbuf_free(struct xen_snd_front_shbuf *buf) 31 + { 32 + int i; 33 + 34 + if (buf->grefs) { 35 + for (i = 0; i < buf->num_grefs; i++) 36 + if (buf->grefs[i] != GRANT_INVALID_REF) 37 + gnttab_end_foreign_access(buf->grefs[i], 38 + 0, 0UL); 39 + kfree(buf->grefs); 40 + } 41 + kfree(buf->directory); 42 + free_pages_exact(buf->buffer, buf->buffer_sz); 43 + xen_snd_front_shbuf_clear(buf); 44 + } 45 + 46 + /* 47 + * number of grant references a page can hold with respect to the 48 + * xensnd_page_directory header 49 + */ 50 + #define XENSND_NUM_GREFS_PER_PAGE ((XEN_PAGE_SIZE - \ 51 + offsetof(struct xensnd_page_directory, gref)) / \ 52 + sizeof(grant_ref_t)) 53 + 54 + static void fill_page_dir(struct xen_snd_front_shbuf *buf, 55 + int num_pages_dir) 56 + { 57 + struct xensnd_page_directory *page_dir; 58 + unsigned char *ptr; 59 + int i, cur_gref, grefs_left, to_copy; 60 + 61 + ptr = buf->directory; 62 + grefs_left = buf->num_grefs - num_pages_dir; 63 + /* 64 + * skip grant references at the beginning, they are for pages granted 65 + * for the page directory itself 66 + */ 67 + cur_gref = num_pages_dir; 68 + for (i = 0; i < num_pages_dir; i++) { 69 + page_dir = (struct xensnd_page_directory *)ptr; 70 + if (grefs_left <= XENSND_NUM_GREFS_PER_PAGE) { 71 + to_copy = grefs_left; 72 + page_dir->gref_dir_next_page = GRANT_INVALID_REF; 73 + } else { 74 + to_copy = XENSND_NUM_GREFS_PER_PAGE; 75 + page_dir->gref_dir_next_page = buf->grefs[i + 1]; 76 + } 77 + 78 + memcpy(&page_dir->gref, &buf->grefs[cur_gref], 79 + to_copy * sizeof(grant_ref_t)); 80 + 81 + ptr += XEN_PAGE_SIZE; 82 + grefs_left -= to_copy; 83 + cur_gref += to_copy; 84 + } 85 + } 86 + 87 + static int grant_references(struct xenbus_device *xb_dev, 88 + struct xen_snd_front_shbuf *buf, 89 + int num_pages_dir, int num_pages_buffer, 90 + int num_grefs) 91 + { 92 + grant_ref_t priv_gref_head; 93 + unsigned long frame; 94 + int ret, i, j, cur_ref; 95 + int otherend_id; 96 + 97 + ret = gnttab_alloc_grant_references(num_grefs, &priv_gref_head); 98 + if (ret) 99 + return ret; 100 + 101 + buf->num_grefs = num_grefs; 102 + otherend_id = xb_dev->otherend_id; 103 + j = 0; 104 + 105 + for (i = 0; i < num_pages_dir; i++) { 106 + cur_ref = gnttab_claim_grant_reference(&priv_gref_head); 107 + if (cur_ref < 0) { 108 + ret = cur_ref; 109 + goto fail; 110 + } 111 + 112 + frame = xen_page_to_gfn(virt_to_page(buf->directory + 113 + XEN_PAGE_SIZE * i)); 114 + gnttab_grant_foreign_access_ref(cur_ref, otherend_id, frame, 0); 115 + buf->grefs[j++] = cur_ref; 116 + } 117 + 118 + for (i = 0; i < num_pages_buffer; i++) { 119 + cur_ref = gnttab_claim_grant_reference(&priv_gref_head); 120 + if (cur_ref < 0) { 121 + ret = cur_ref; 122 + goto fail; 123 + } 124 + 125 + frame = xen_page_to_gfn(virt_to_page(buf->buffer + 126 + XEN_PAGE_SIZE * i)); 127 + gnttab_grant_foreign_access_ref(cur_ref, otherend_id, frame, 0); 128 + buf->grefs[j++] = cur_ref; 129 + } 130 + 131 + gnttab_free_grant_references(priv_gref_head); 132 + fill_page_dir(buf, num_pages_dir); 133 + return 0; 134 + 135 + fail: 136 + gnttab_free_grant_references(priv_gref_head); 137 + return ret; 138 + } 139 + 140 + static int alloc_int_buffers(struct xen_snd_front_shbuf *buf, 141 + int num_pages_dir, int num_pages_buffer, 142 + int num_grefs) 143 + { 144 + buf->grefs = kcalloc(num_grefs, sizeof(*buf->grefs), GFP_KERNEL); 145 + if (!buf->grefs) 146 + return -ENOMEM; 147 + 148 + buf->directory = kcalloc(num_pages_dir, XEN_PAGE_SIZE, GFP_KERNEL); 149 + if (!buf->directory) 150 + goto fail; 151 + 152 + buf->buffer_sz = num_pages_buffer * XEN_PAGE_SIZE; 153 + buf->buffer = alloc_pages_exact(buf->buffer_sz, GFP_KERNEL); 154 + if (!buf->buffer) 155 + goto fail; 156 + 157 + return 0; 158 + 159 + fail: 160 + kfree(buf->grefs); 161 + buf->grefs = NULL; 162 + kfree(buf->directory); 163 + buf->directory = NULL; 164 + return -ENOMEM; 165 + } 166 + 167 + int xen_snd_front_shbuf_alloc(struct xenbus_device *xb_dev, 168 + struct xen_snd_front_shbuf *buf, 169 + unsigned int buffer_sz) 170 + { 171 + int num_pages_buffer, num_pages_dir, num_grefs; 172 + int ret; 173 + 174 + xen_snd_front_shbuf_clear(buf); 175 + 176 + num_pages_buffer = DIV_ROUND_UP(buffer_sz, XEN_PAGE_SIZE); 177 + /* number of pages the page directory consumes itself */ 178 + num_pages_dir = DIV_ROUND_UP(num_pages_buffer, 179 + XENSND_NUM_GREFS_PER_PAGE); 180 + num_grefs = num_pages_buffer + num_pages_dir; 181 + 182 + ret = alloc_int_buffers(buf, num_pages_dir, 183 + num_pages_buffer, num_grefs); 184 + if (ret < 0) 185 + return ret; 186 + 187 + ret = grant_references(xb_dev, buf, num_pages_dir, num_pages_buffer, 188 + num_grefs); 189 + if (ret < 0) 190 + return ret; 191 + 192 + fill_page_dir(buf, num_pages_dir); 193 + return 0; 194 + }
+36
sound/xen/xen_snd_front_shbuf.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 OR MIT */ 2 + 3 + /* 4 + * Xen para-virtual sound device 5 + * 6 + * Copyright (C) 2016-2018 EPAM Systems Inc. 7 + * 8 + * Author: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com> 9 + */ 10 + 11 + #ifndef __XEN_SND_FRONT_SHBUF_H 12 + #define __XEN_SND_FRONT_SHBUF_H 13 + 14 + #include <xen/grant_table.h> 15 + 16 + #include "xen_snd_front_evtchnl.h" 17 + 18 + struct xen_snd_front_shbuf { 19 + int num_grefs; 20 + grant_ref_t *grefs; 21 + u8 *directory; 22 + u8 *buffer; 23 + size_t buffer_sz; 24 + }; 25 + 26 + grant_ref_t xen_snd_front_shbuf_get_dir_start(struct xen_snd_front_shbuf *buf); 27 + 28 + int xen_snd_front_shbuf_alloc(struct xenbus_device *xb_dev, 29 + struct xen_snd_front_shbuf *buf, 30 + unsigned int buffer_sz); 31 + 32 + void xen_snd_front_shbuf_clear(struct xen_snd_front_shbuf *buf); 33 + 34 + void xen_snd_front_shbuf_free(struct xen_snd_front_shbuf *buf); 35 + 36 + #endif /* __XEN_SND_FRONT_SHBUF_H */