diff options
Diffstat (limited to 'sound/xen/xen_snd_front_shbuf.c')
-rw-r--r-- | sound/xen/xen_snd_front_shbuf.c | 194 |
1 files changed, 194 insertions, 0 deletions
diff --git a/sound/xen/xen_snd_front_shbuf.c b/sound/xen/xen_snd_front_shbuf.c new file mode 100644 index 000000000000..07ac176a41ba --- /dev/null +++ b/sound/xen/xen_snd_front_shbuf.c | |||
@@ -0,0 +1,194 @@ | |||
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 | } | ||