1 // SPDX-License-Identifier: GPL-2.0 OR MIT
4 * Xen para-virtual sound device
6 * Copyright (C) 2016-2018 EPAM Systems Inc.
8 * Author: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>
11 #include <linux/kernel.h>
13 #include <xen/xenbus.h>
15 #include "xen_snd_front_shbuf.h"
17 grant_ref_t
xen_snd_front_shbuf_get_dir_start(struct xen_snd_front_shbuf
*buf
)
20 return GRANT_INVALID_REF
;
25 void xen_snd_front_shbuf_clear(struct xen_snd_front_shbuf
*buf
)
27 memset(buf
, 0, sizeof(*buf
));
30 void xen_snd_front_shbuf_free(struct xen_snd_front_shbuf
*buf
)
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
],
41 kfree(buf
->directory
);
42 free_pages_exact(buf
->buffer
, buf
->buffer_sz
);
43 xen_snd_front_shbuf_clear(buf
);
47 * number of grant references a page can hold with respect to the
48 * xensnd_page_directory header
50 #define XENSND_NUM_GREFS_PER_PAGE ((XEN_PAGE_SIZE - \
51 offsetof(struct xensnd_page_directory, gref)) / \
54 static void fill_page_dir(struct xen_snd_front_shbuf
*buf
,
57 struct xensnd_page_directory
*page_dir
;
59 int i
, cur_gref
, grefs_left
, to_copy
;
62 grefs_left
= buf
->num_grefs
- num_pages_dir
;
64 * skip grant references at the beginning, they are for pages granted
65 * for the page directory itself
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
) {
72 page_dir
->gref_dir_next_page
= GRANT_INVALID_REF
;
74 to_copy
= XENSND_NUM_GREFS_PER_PAGE
;
75 page_dir
->gref_dir_next_page
= buf
->grefs
[i
+ 1];
78 memcpy(&page_dir
->gref
, &buf
->grefs
[cur_gref
],
79 to_copy
* sizeof(grant_ref_t
));
82 grefs_left
-= to_copy
;
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
,
92 grant_ref_t priv_gref_head
;
94 int ret
, i
, j
, cur_ref
;
97 ret
= gnttab_alloc_grant_references(num_grefs
, &priv_gref_head
);
101 buf
->num_grefs
= num_grefs
;
102 otherend_id
= xb_dev
->otherend_id
;
105 for (i
= 0; i
< num_pages_dir
; i
++) {
106 cur_ref
= gnttab_claim_grant_reference(&priv_gref_head
);
112 frame
= xen_page_to_gfn(virt_to_page(buf
->directory
+
114 gnttab_grant_foreign_access_ref(cur_ref
, otherend_id
, frame
, 0);
115 buf
->grefs
[j
++] = cur_ref
;
118 for (i
= 0; i
< num_pages_buffer
; i
++) {
119 cur_ref
= gnttab_claim_grant_reference(&priv_gref_head
);
125 frame
= xen_page_to_gfn(virt_to_page(buf
->buffer
+
127 gnttab_grant_foreign_access_ref(cur_ref
, otherend_id
, frame
, 0);
128 buf
->grefs
[j
++] = cur_ref
;
131 gnttab_free_grant_references(priv_gref_head
);
132 fill_page_dir(buf
, num_pages_dir
);
136 gnttab_free_grant_references(priv_gref_head
);
140 static int alloc_int_buffers(struct xen_snd_front_shbuf
*buf
,
141 int num_pages_dir
, int num_pages_buffer
,
144 buf
->grefs
= kcalloc(num_grefs
, sizeof(*buf
->grefs
), GFP_KERNEL
);
148 buf
->directory
= kcalloc(num_pages_dir
, XEN_PAGE_SIZE
, GFP_KERNEL
);
152 buf
->buffer_sz
= num_pages_buffer
* XEN_PAGE_SIZE
;
153 buf
->buffer
= alloc_pages_exact(buf
->buffer_sz
, GFP_KERNEL
);
162 kfree(buf
->directory
);
163 buf
->directory
= NULL
;
167 int xen_snd_front_shbuf_alloc(struct xenbus_device
*xb_dev
,
168 struct xen_snd_front_shbuf
*buf
,
169 unsigned int buffer_sz
)
171 int num_pages_buffer
, num_pages_dir
, num_grefs
;
174 xen_snd_front_shbuf_clear(buf
);
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
;
182 ret
= alloc_int_buffers(buf
, num_pages_dir
,
183 num_pages_buffer
, num_grefs
);
187 ret
= grant_references(xb_dev
, buf
, num_pages_dir
, num_pages_buffer
,
192 fill_page_dir(buf
, num_pages_dir
);