mmap.c 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. #define _GNU_SOURCE
  3. #include <dirent.h>
  4. #include <sched.h>
  5. #include <stdbool.h>
  6. #include <stdio.h>
  7. #include <unistd.h>
  8. #include <sys/ioctl.h>
  9. #include <sys/mman.h>
  10. #include <sys/syscall.h>
  11. #include <sys/types.h>
  12. #include <linux/perf_event.h>
  13. #include "kselftest_harness.h"
  14. #define RB_SIZE 0x3000
  15. #define AUX_SIZE 0x10000
  16. #define AUX_OFFS 0x4000
  17. #define HOLE_SIZE 0x1000
  18. /* Reserve space for rb, aux with space for shrink-beyond-vma testing. */
  19. #define REGION_SIZE (2 * RB_SIZE + 2 * AUX_SIZE)
  20. #define REGION_AUX_OFFS (2 * RB_SIZE)
  21. #define MAP_BASE 1
  22. #define MAP_AUX 2
  23. #define EVENT_SRC_DIR "/sys/bus/event_source/devices"
  24. FIXTURE(perf_mmap)
  25. {
  26. int fd;
  27. void *ptr;
  28. void *region;
  29. };
  30. FIXTURE_VARIANT(perf_mmap)
  31. {
  32. bool aux;
  33. unsigned long ptr_size;
  34. };
  35. FIXTURE_VARIANT_ADD(perf_mmap, rb)
  36. {
  37. .aux = false,
  38. .ptr_size = RB_SIZE,
  39. };
  40. FIXTURE_VARIANT_ADD(perf_mmap, aux)
  41. {
  42. .aux = true,
  43. .ptr_size = AUX_SIZE,
  44. };
  45. static bool read_event_type(struct dirent *dent, __u32 *type)
  46. {
  47. char typefn[512];
  48. FILE *fp;
  49. int res;
  50. snprintf(typefn, sizeof(typefn), "%s/%s/type", EVENT_SRC_DIR, dent->d_name);
  51. fp = fopen(typefn, "r");
  52. if (!fp)
  53. return false;
  54. res = fscanf(fp, "%u", type);
  55. fclose(fp);
  56. return res > 0;
  57. }
  58. FIXTURE_SETUP(perf_mmap)
  59. {
  60. struct perf_event_attr attr = {
  61. .size = sizeof(attr),
  62. .disabled = 1,
  63. .exclude_kernel = 1,
  64. .exclude_hv = 1,
  65. };
  66. struct perf_event_attr attr_ok = {};
  67. unsigned int eacces = 0, map = 0;
  68. struct perf_event_mmap_page *rb;
  69. struct dirent *dent;
  70. void *aux, *region;
  71. DIR *dir;
  72. self->ptr = NULL;
  73. dir = opendir(EVENT_SRC_DIR);
  74. if (!dir)
  75. SKIP(return, "perf not available.");
  76. region = mmap(NULL, REGION_SIZE, PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0);
  77. ASSERT_NE(region, MAP_FAILED);
  78. self->region = region;
  79. // Try to find a suitable event on this system
  80. while ((dent = readdir(dir))) {
  81. int fd;
  82. if (!read_event_type(dent, &attr.type))
  83. continue;
  84. fd = syscall(SYS_perf_event_open, &attr, 0, -1, -1, 0);
  85. if (fd < 0) {
  86. if (errno == EACCES)
  87. eacces++;
  88. continue;
  89. }
  90. // Check whether the event supports mmap()
  91. rb = mmap(region, RB_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd, 0);
  92. if (rb == MAP_FAILED) {
  93. close(fd);
  94. continue;
  95. }
  96. if (!map) {
  97. // Save the event in case that no AUX capable event is found
  98. attr_ok = attr;
  99. map = MAP_BASE;
  100. }
  101. if (!variant->aux)
  102. continue;
  103. rb->aux_offset = AUX_OFFS;
  104. rb->aux_size = AUX_SIZE;
  105. // Check whether it supports a AUX buffer
  106. aux = mmap(region + REGION_AUX_OFFS, AUX_SIZE, PROT_READ | PROT_WRITE,
  107. MAP_SHARED | MAP_FIXED, fd, AUX_OFFS);
  108. if (aux == MAP_FAILED) {
  109. munmap(rb, RB_SIZE);
  110. close(fd);
  111. continue;
  112. }
  113. attr_ok = attr;
  114. map = MAP_AUX;
  115. munmap(aux, AUX_SIZE);
  116. munmap(rb, RB_SIZE);
  117. close(fd);
  118. break;
  119. }
  120. closedir(dir);
  121. if (!map) {
  122. if (!eacces)
  123. SKIP(return, "No mappable perf event found.");
  124. else
  125. SKIP(return, "No permissions for perf_event_open()");
  126. }
  127. self->fd = syscall(SYS_perf_event_open, &attr_ok, 0, -1, -1, 0);
  128. ASSERT_NE(self->fd, -1);
  129. rb = mmap(region, RB_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, self->fd, 0);
  130. ASSERT_NE(rb, MAP_FAILED);
  131. if (!variant->aux) {
  132. self->ptr = rb;
  133. return;
  134. }
  135. if (map != MAP_AUX)
  136. SKIP(return, "No AUX event found.");
  137. rb->aux_offset = AUX_OFFS;
  138. rb->aux_size = AUX_SIZE;
  139. aux = mmap(region + REGION_AUX_OFFS, AUX_SIZE, PROT_READ | PROT_WRITE,
  140. MAP_SHARED | MAP_FIXED, self->fd, AUX_OFFS);
  141. ASSERT_NE(aux, MAP_FAILED);
  142. self->ptr = aux;
  143. }
  144. FIXTURE_TEARDOWN(perf_mmap)
  145. {
  146. ASSERT_EQ(munmap(self->region, REGION_SIZE), 0);
  147. if (self->fd != -1)
  148. ASSERT_EQ(close(self->fd), 0);
  149. }
  150. TEST_F(perf_mmap, remap)
  151. {
  152. void *tmp, *ptr = self->ptr;
  153. unsigned long size = variant->ptr_size;
  154. // Test the invalid remaps
  155. ASSERT_EQ(mremap(ptr, size, HOLE_SIZE, MREMAP_MAYMOVE), MAP_FAILED);
  156. ASSERT_EQ(mremap(ptr + HOLE_SIZE, size, HOLE_SIZE, MREMAP_MAYMOVE), MAP_FAILED);
  157. ASSERT_EQ(mremap(ptr + size - HOLE_SIZE, HOLE_SIZE, size, MREMAP_MAYMOVE), MAP_FAILED);
  158. // Shrink the end of the mapping such that we only unmap past end of the VMA,
  159. // which should succeed and poke a hole into the PROT_NONE region
  160. ASSERT_NE(mremap(ptr + size - HOLE_SIZE, size, HOLE_SIZE, MREMAP_MAYMOVE), MAP_FAILED);
  161. // Remap the whole buffer to a new address
  162. tmp = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
  163. ASSERT_NE(tmp, MAP_FAILED);
  164. // Try splitting offset 1 hole size into VMA, this should fail
  165. ASSERT_EQ(mremap(ptr + HOLE_SIZE, size - HOLE_SIZE, size - HOLE_SIZE,
  166. MREMAP_MAYMOVE | MREMAP_FIXED, tmp), MAP_FAILED);
  167. // Remapping the whole thing should succeed fine
  168. ptr = mremap(ptr, size, size, MREMAP_MAYMOVE | MREMAP_FIXED, tmp);
  169. ASSERT_EQ(ptr, tmp);
  170. ASSERT_EQ(munmap(tmp, size), 0);
  171. }
  172. TEST_F(perf_mmap, unmap)
  173. {
  174. unsigned long size = variant->ptr_size;
  175. // Try to poke holes into the mappings
  176. ASSERT_NE(munmap(self->ptr, HOLE_SIZE), 0);
  177. ASSERT_NE(munmap(self->ptr + HOLE_SIZE, HOLE_SIZE), 0);
  178. ASSERT_NE(munmap(self->ptr + size - HOLE_SIZE, HOLE_SIZE), 0);
  179. }
  180. TEST_F(perf_mmap, map)
  181. {
  182. unsigned long size = variant->ptr_size;
  183. // Try to poke holes into the mappings by mapping anonymous memory over it
  184. ASSERT_EQ(mmap(self->ptr, HOLE_SIZE, PROT_READ | PROT_WRITE,
  185. MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0), MAP_FAILED);
  186. ASSERT_EQ(mmap(self->ptr + HOLE_SIZE, HOLE_SIZE, PROT_READ | PROT_WRITE,
  187. MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0), MAP_FAILED);
  188. ASSERT_EQ(mmap(self->ptr + size - HOLE_SIZE, HOLE_SIZE, PROT_READ | PROT_WRITE,
  189. MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0), MAP_FAILED);
  190. }
  191. TEST_HARNESS_MAIN