xen_snd_front_shbuf.c 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. // SPDX-License-Identifier: GPL-2.0 OR MIT
  2. /*
  3. * Xen para-virtual sound device
  4. *
  5. * Copyright (C) 2016-2018 EPAM Systems Inc.
  6. *
  7. * Author: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>
  8. */
  9. #include <linux/kernel.h>
  10. #include <xen/xen.h>
  11. #include <xen/xenbus.h>
  12. #include "xen_snd_front_shbuf.h"
  13. grant_ref_t xen_snd_front_shbuf_get_dir_start(struct xen_snd_front_shbuf *buf)
  14. {
  15. if (!buf->grefs)
  16. return GRANT_INVALID_REF;
  17. return buf->grefs[0];
  18. }
  19. void xen_snd_front_shbuf_clear(struct xen_snd_front_shbuf *buf)
  20. {
  21. memset(buf, 0, sizeof(*buf));
  22. }
  23. void xen_snd_front_shbuf_free(struct xen_snd_front_shbuf *buf)
  24. {
  25. int i;
  26. if (buf->grefs) {
  27. for (i = 0; i < buf->num_grefs; i++)
  28. if (buf->grefs[i] != GRANT_INVALID_REF)
  29. gnttab_end_foreign_access(buf->grefs[i],
  30. 0, 0UL);
  31. kfree(buf->grefs);
  32. }
  33. kfree(buf->directory);
  34. free_pages_exact(buf->buffer, buf->buffer_sz);
  35. xen_snd_front_shbuf_clear(buf);
  36. }
  37. /*
  38. * number of grant references a page can hold with respect to the
  39. * xensnd_page_directory header
  40. */
  41. #define XENSND_NUM_GREFS_PER_PAGE ((XEN_PAGE_SIZE - \
  42. offsetof(struct xensnd_page_directory, gref)) / \
  43. sizeof(grant_ref_t))
  44. static void fill_page_dir(struct xen_snd_front_shbuf *buf,
  45. int num_pages_dir)
  46. {
  47. struct xensnd_page_directory *page_dir;
  48. unsigned char *ptr;
  49. int i, cur_gref, grefs_left, to_copy;
  50. ptr = buf->directory;
  51. grefs_left = buf->num_grefs - num_pages_dir;
  52. /*
  53. * skip grant references at the beginning, they are for pages granted
  54. * for the page directory itself
  55. */
  56. cur_gref = num_pages_dir;
  57. for (i = 0; i < num_pages_dir; i++) {
  58. page_dir = (struct xensnd_page_directory *)ptr;
  59. if (grefs_left <= XENSND_NUM_GREFS_PER_PAGE) {
  60. to_copy = grefs_left;
  61. page_dir->gref_dir_next_page = GRANT_INVALID_REF;
  62. } else {
  63. to_copy = XENSND_NUM_GREFS_PER_PAGE;
  64. page_dir->gref_dir_next_page = buf->grefs[i + 1];
  65. }
  66. memcpy(&page_dir->gref, &buf->grefs[cur_gref],
  67. to_copy * sizeof(grant_ref_t));
  68. ptr += XEN_PAGE_SIZE;
  69. grefs_left -= to_copy;
  70. cur_gref += to_copy;
  71. }
  72. }
  73. static int grant_references(struct xenbus_device *xb_dev,
  74. struct xen_snd_front_shbuf *buf,
  75. int num_pages_dir, int num_pages_buffer,
  76. int num_grefs)
  77. {
  78. grant_ref_t priv_gref_head;
  79. unsigned long frame;
  80. int ret, i, j, cur_ref;
  81. int otherend_id;
  82. ret = gnttab_alloc_grant_references(num_grefs, &priv_gref_head);
  83. if (ret)
  84. return ret;
  85. buf->num_grefs = num_grefs;
  86. otherend_id = xb_dev->otherend_id;
  87. j = 0;
  88. for (i = 0; i < num_pages_dir; i++) {
  89. cur_ref = gnttab_claim_grant_reference(&priv_gref_head);
  90. if (cur_ref < 0) {
  91. ret = cur_ref;
  92. goto fail;
  93. }
  94. frame = xen_page_to_gfn(virt_to_page(buf->directory +
  95. XEN_PAGE_SIZE * i));
  96. gnttab_grant_foreign_access_ref(cur_ref, otherend_id, frame, 0);
  97. buf->grefs[j++] = cur_ref;
  98. }
  99. for (i = 0; i < num_pages_buffer; i++) {
  100. cur_ref = gnttab_claim_grant_reference(&priv_gref_head);
  101. if (cur_ref < 0) {
  102. ret = cur_ref;
  103. goto fail;
  104. }
  105. frame = xen_page_to_gfn(virt_to_page(buf->buffer +
  106. XEN_PAGE_SIZE * i));
  107. gnttab_grant_foreign_access_ref(cur_ref, otherend_id, frame, 0);
  108. buf->grefs[j++] = cur_ref;
  109. }
  110. gnttab_free_grant_references(priv_gref_head);
  111. fill_page_dir(buf, num_pages_dir);
  112. return 0;
  113. fail:
  114. gnttab_free_grant_references(priv_gref_head);
  115. return ret;
  116. }
  117. static int alloc_int_buffers(struct xen_snd_front_shbuf *buf,
  118. int num_pages_dir, int num_pages_buffer,
  119. int num_grefs)
  120. {
  121. buf->grefs = kcalloc(num_grefs, sizeof(*buf->grefs), GFP_KERNEL);
  122. if (!buf->grefs)
  123. return -ENOMEM;
  124. buf->directory = kcalloc(num_pages_dir, XEN_PAGE_SIZE, GFP_KERNEL);
  125. if (!buf->directory)
  126. goto fail;
  127. buf->buffer_sz = num_pages_buffer * XEN_PAGE_SIZE;
  128. buf->buffer = alloc_pages_exact(buf->buffer_sz, GFP_KERNEL);
  129. if (!buf->buffer)
  130. goto fail;
  131. return 0;
  132. fail:
  133. kfree(buf->grefs);
  134. buf->grefs = NULL;
  135. kfree(buf->directory);
  136. buf->directory = NULL;
  137. return -ENOMEM;
  138. }
  139. int xen_snd_front_shbuf_alloc(struct xenbus_device *xb_dev,
  140. struct xen_snd_front_shbuf *buf,
  141. unsigned int buffer_sz)
  142. {
  143. int num_pages_buffer, num_pages_dir, num_grefs;
  144. int ret;
  145. xen_snd_front_shbuf_clear(buf);
  146. num_pages_buffer = DIV_ROUND_UP(buffer_sz, XEN_PAGE_SIZE);
  147. /* number of pages the page directory consumes itself */
  148. num_pages_dir = DIV_ROUND_UP(num_pages_buffer,
  149. XENSND_NUM_GREFS_PER_PAGE);
  150. num_grefs = num_pages_buffer + num_pages_dir;
  151. ret = alloc_int_buffers(buf, num_pages_dir,
  152. num_pages_buffer, num_grefs);
  153. if (ret < 0)
  154. return ret;
  155. ret = grant_references(xb_dev, buf, num_pages_dir, num_pages_buffer,
  156. num_grefs);
  157. if (ret < 0)
  158. return ret;
  159. fill_page_dir(buf, num_pages_dir);
  160. return 0;
  161. }