memfd.c 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. /*
  2. * memfd.c
  3. *
  4. * Copyright (c) 2015 Red Hat, Inc.
  5. *
  6. * QEMU library functions on POSIX which are shared between QEMU and
  7. * the QEMU tools.
  8. *
  9. * Permission is hereby granted, free of charge, to any person obtaining a copy
  10. * of this software and associated documentation files (the "Software"), to deal
  11. * in the Software without restriction, including without limitation the rights
  12. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  13. * copies of the Software, and to permit persons to whom the Software is
  14. * furnished to do so, subject to the following conditions:
  15. *
  16. * The above copyright notice and this permission notice shall be included in
  17. * all copies or substantial portions of the Software.
  18. *
  19. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  20. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  21. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  22. * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  23. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  24. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  25. * THE SOFTWARE.
  26. */
  27. #include "qemu/osdep.h"
  28. #include "qapi/error.h"
  29. #include "qemu/memfd.h"
  30. #include "qemu/host-utils.h"
  31. #if defined CONFIG_LINUX && !defined CONFIG_MEMFD
  32. #include <sys/syscall.h>
  33. #include <asm/unistd.h>
  34. int memfd_create(const char *name, unsigned int flags)
  35. {
  36. #ifdef __NR_memfd_create
  37. return syscall(__NR_memfd_create, name, flags);
  38. #else
  39. errno = ENOSYS;
  40. return -1;
  41. #endif
  42. }
  43. #endif
  44. int qemu_memfd_create(const char *name, size_t size, bool hugetlb,
  45. uint64_t hugetlbsize, unsigned int seals, Error **errp)
  46. {
  47. int htsize = hugetlbsize ? ctz64(hugetlbsize) : 0;
  48. if (htsize && 1ULL << htsize != hugetlbsize) {
  49. error_setg(errp, "Hugepage size must be a power of 2");
  50. return -1;
  51. }
  52. htsize = htsize << MFD_HUGE_SHIFT;
  53. #ifdef CONFIG_LINUX
  54. int mfd = -1;
  55. unsigned int flags = MFD_CLOEXEC;
  56. if (seals) {
  57. flags |= MFD_ALLOW_SEALING;
  58. }
  59. if (hugetlb) {
  60. flags |= MFD_HUGETLB;
  61. flags |= htsize;
  62. }
  63. mfd = memfd_create(name, flags);
  64. if (mfd < 0) {
  65. error_setg_errno(errp, errno,
  66. "failed to create memfd with flags 0x%x", flags);
  67. goto err;
  68. }
  69. if (ftruncate(mfd, size) == -1) {
  70. error_setg_errno(errp, errno, "failed to resize memfd to %zu", size);
  71. goto err;
  72. }
  73. if (seals && fcntl(mfd, F_ADD_SEALS, seals) == -1) {
  74. error_setg_errno(errp, errno, "failed to add seals 0x%x", seals);
  75. goto err;
  76. }
  77. return mfd;
  78. err:
  79. if (mfd >= 0) {
  80. close(mfd);
  81. }
  82. #else
  83. error_setg_errno(errp, ENOSYS, "failed to create memfd");
  84. #endif
  85. return -1;
  86. }
  87. /*
  88. * This is a best-effort helper for shared memory allocation, with
  89. * optional sealing. The helper will do his best to allocate using
  90. * memfd with sealing, but may fallback on other methods without
  91. * sealing.
  92. */
  93. void *qemu_memfd_alloc(const char *name, size_t size, unsigned int seals,
  94. int *fd, Error **errp)
  95. {
  96. void *ptr;
  97. int mfd = qemu_memfd_create(name, size, false, 0, seals, NULL);
  98. /* some systems have memfd without sealing */
  99. if (mfd == -1) {
  100. mfd = qemu_memfd_create(name, size, false, 0, 0, NULL);
  101. }
  102. if (mfd == -1) {
  103. const char *tmpdir = g_get_tmp_dir();
  104. gchar *fname;
  105. fname = g_strdup_printf("%s/memfd-XXXXXX", tmpdir);
  106. mfd = mkstemp(fname);
  107. unlink(fname);
  108. g_free(fname);
  109. if (mfd == -1 ||
  110. ftruncate(mfd, size) == -1) {
  111. goto err;
  112. }
  113. }
  114. ptr = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, mfd, 0);
  115. if (ptr == MAP_FAILED) {
  116. goto err;
  117. }
  118. *fd = mfd;
  119. return ptr;
  120. err:
  121. error_setg_errno(errp, errno, "failed to allocate shared memory");
  122. if (mfd >= 0) {
  123. close(mfd);
  124. }
  125. return NULL;
  126. }
  127. void qemu_memfd_free(void *ptr, size_t size, int fd)
  128. {
  129. if (ptr) {
  130. munmap(ptr, size);
  131. }
  132. if (fd != -1) {
  133. close(fd);
  134. }
  135. }
  136. enum {
  137. MEMFD_KO,
  138. MEMFD_OK,
  139. MEMFD_TODO
  140. };
  141. /**
  142. * qemu_memfd_alloc_check():
  143. *
  144. * Check if qemu_memfd_alloc() can allocate, including using a
  145. * fallback implementation when host doesn't support memfd.
  146. */
  147. bool qemu_memfd_alloc_check(void)
  148. {
  149. static int memfd_check = MEMFD_TODO;
  150. if (memfd_check == MEMFD_TODO) {
  151. int fd;
  152. void *ptr;
  153. fd = -1;
  154. ptr = qemu_memfd_alloc("test", 4096, 0, &fd, NULL);
  155. memfd_check = ptr ? MEMFD_OK : MEMFD_KO;
  156. qemu_memfd_free(ptr, 4096, fd);
  157. }
  158. return memfd_check == MEMFD_OK;
  159. }
  160. /**
  161. * qemu_memfd_check():
  162. *
  163. * Check if host supports memfd.
  164. */
  165. bool qemu_memfd_check(unsigned int flags)
  166. {
  167. #ifdef CONFIG_LINUX
  168. int mfd = memfd_create("test", flags | MFD_CLOEXEC);
  169. if (mfd >= 0) {
  170. close(mfd);
  171. return true;
  172. }
  173. #endif
  174. return false;
  175. }