cpumap.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. #include <errno.h>
  3. #include <perf/cpumap.h>
  4. #include <stdlib.h>
  5. #include <linux/refcount.h>
  6. #include <internal/cpumap.h>
  7. #include <asm/bug.h>
  8. #include <stdio.h>
  9. #include <string.h>
  10. #include <unistd.h>
  11. #include <ctype.h>
  12. #include <limits.h>
  13. #include "internal.h"
  14. #include <api/fs/fs.h>
  15. #define MAX_NR_CPUS 4096
  16. void perf_cpu_map__set_nr(struct perf_cpu_map *map, int nr_cpus)
  17. {
  18. RC_CHK_ACCESS(map)->nr = nr_cpus;
  19. }
  20. struct perf_cpu_map *perf_cpu_map__alloc(int nr_cpus)
  21. {
  22. RC_STRUCT(perf_cpu_map) *cpus;
  23. struct perf_cpu_map *result;
  24. if (nr_cpus == 0)
  25. return NULL;
  26. cpus = malloc(sizeof(*cpus) + sizeof(struct perf_cpu) * nr_cpus);
  27. if (ADD_RC_CHK(result, cpus)) {
  28. cpus->nr = nr_cpus;
  29. refcount_set(&cpus->refcnt, 1);
  30. }
  31. return result;
  32. }
  33. struct perf_cpu_map *perf_cpu_map__new_any_cpu(void)
  34. {
  35. struct perf_cpu_map *cpus = perf_cpu_map__alloc(1);
  36. if (cpus)
  37. RC_CHK_ACCESS(cpus)->map[0].cpu = -1;
  38. return cpus;
  39. }
  40. static void cpu_map__delete(struct perf_cpu_map *map)
  41. {
  42. if (map) {
  43. WARN_ONCE(refcount_read(perf_cpu_map__refcnt(map)) != 0,
  44. "cpu_map refcnt unbalanced\n");
  45. RC_CHK_FREE(map);
  46. }
  47. }
  48. struct perf_cpu_map *perf_cpu_map__get(struct perf_cpu_map *map)
  49. {
  50. struct perf_cpu_map *result;
  51. if (RC_CHK_GET(result, map))
  52. refcount_inc(perf_cpu_map__refcnt(map));
  53. return result;
  54. }
  55. void perf_cpu_map__put(struct perf_cpu_map *map)
  56. {
  57. if (map) {
  58. if (refcount_dec_and_test(perf_cpu_map__refcnt(map)))
  59. cpu_map__delete(map);
  60. else
  61. RC_CHK_PUT(map);
  62. }
  63. }
  64. static struct perf_cpu_map *cpu_map__new_sysconf(void)
  65. {
  66. struct perf_cpu_map *cpus;
  67. int nr_cpus, nr_cpus_conf;
  68. nr_cpus = sysconf(_SC_NPROCESSORS_ONLN);
  69. if (nr_cpus < 0)
  70. return NULL;
  71. nr_cpus_conf = sysconf(_SC_NPROCESSORS_CONF);
  72. if (nr_cpus != nr_cpus_conf) {
  73. pr_warning("Number of online CPUs (%d) differs from the number configured (%d) the CPU map will only cover the first %d CPUs.",
  74. nr_cpus, nr_cpus_conf, nr_cpus);
  75. }
  76. cpus = perf_cpu_map__alloc(nr_cpus);
  77. if (cpus != NULL) {
  78. int i;
  79. for (i = 0; i < nr_cpus; ++i)
  80. RC_CHK_ACCESS(cpus)->map[i].cpu = i;
  81. }
  82. return cpus;
  83. }
  84. static struct perf_cpu_map *cpu_map__new_sysfs_online(void)
  85. {
  86. struct perf_cpu_map *cpus = NULL;
  87. char *buf = NULL;
  88. size_t buf_len;
  89. if (sysfs__read_str("devices/system/cpu/online", &buf, &buf_len) >= 0) {
  90. cpus = perf_cpu_map__new(buf);
  91. free(buf);
  92. }
  93. return cpus;
  94. }
  95. struct perf_cpu_map *perf_cpu_map__new_online_cpus(void)
  96. {
  97. struct perf_cpu_map *cpus = cpu_map__new_sysfs_online();
  98. if (cpus)
  99. return cpus;
  100. return cpu_map__new_sysconf();
  101. }
  102. static int cmp_cpu(const void *a, const void *b)
  103. {
  104. const struct perf_cpu *cpu_a = a, *cpu_b = b;
  105. return cpu_a->cpu - cpu_b->cpu;
  106. }
  107. static struct perf_cpu __perf_cpu_map__cpu(const struct perf_cpu_map *cpus, int idx)
  108. {
  109. return RC_CHK_ACCESS(cpus)->map[idx];
  110. }
  111. static struct perf_cpu_map *cpu_map__trim_new(int nr_cpus, const struct perf_cpu *tmp_cpus)
  112. {
  113. size_t payload_size = nr_cpus * sizeof(struct perf_cpu);
  114. struct perf_cpu_map *cpus = perf_cpu_map__alloc(nr_cpus);
  115. int i, j;
  116. if (cpus != NULL) {
  117. memcpy(RC_CHK_ACCESS(cpus)->map, tmp_cpus, payload_size);
  118. qsort(RC_CHK_ACCESS(cpus)->map, nr_cpus, sizeof(struct perf_cpu), cmp_cpu);
  119. /* Remove dups */
  120. j = 0;
  121. for (i = 0; i < nr_cpus; i++) {
  122. if (i == 0 ||
  123. __perf_cpu_map__cpu(cpus, i).cpu !=
  124. __perf_cpu_map__cpu(cpus, i - 1).cpu) {
  125. RC_CHK_ACCESS(cpus)->map[j++].cpu =
  126. __perf_cpu_map__cpu(cpus, i).cpu;
  127. }
  128. }
  129. perf_cpu_map__set_nr(cpus, j);
  130. assert(j <= nr_cpus);
  131. }
  132. return cpus;
  133. }
  134. struct perf_cpu_map *perf_cpu_map__new(const char *cpu_list)
  135. {
  136. struct perf_cpu_map *cpus = NULL;
  137. unsigned long start_cpu, end_cpu = 0;
  138. char *p = NULL;
  139. int i, nr_cpus = 0;
  140. struct perf_cpu *tmp_cpus = NULL, *tmp;
  141. int max_entries = 0;
  142. if (!cpu_list)
  143. return perf_cpu_map__new_online_cpus();
  144. /*
  145. * must handle the case of empty cpumap to cover
  146. * TOPOLOGY header for NUMA nodes with no CPU
  147. * ( e.g., because of CPU hotplug)
  148. */
  149. if (!isdigit(*cpu_list) && *cpu_list != '\0')
  150. goto out;
  151. while (isdigit(*cpu_list)) {
  152. p = NULL;
  153. start_cpu = strtoul(cpu_list, &p, 0);
  154. if (start_cpu >= INT16_MAX
  155. || (*p != '\0' && *p != ',' && *p != '-' && *p != '\n'))
  156. goto invalid;
  157. if (*p == '-') {
  158. cpu_list = ++p;
  159. p = NULL;
  160. end_cpu = strtoul(cpu_list, &p, 0);
  161. if (end_cpu >= INT16_MAX || (*p != '\0' && *p != ',' && *p != '\n'))
  162. goto invalid;
  163. if (end_cpu < start_cpu)
  164. goto invalid;
  165. } else {
  166. end_cpu = start_cpu;
  167. }
  168. WARN_ONCE(end_cpu >= MAX_NR_CPUS, "Perf can support %d CPUs. "
  169. "Consider raising MAX_NR_CPUS\n", MAX_NR_CPUS);
  170. for (; start_cpu <= end_cpu; start_cpu++) {
  171. /* check for duplicates */
  172. for (i = 0; i < nr_cpus; i++)
  173. if (tmp_cpus[i].cpu == (int16_t)start_cpu)
  174. goto invalid;
  175. if (nr_cpus == max_entries) {
  176. max_entries += max(end_cpu - start_cpu + 1, 16UL);
  177. tmp = realloc(tmp_cpus, max_entries * sizeof(struct perf_cpu));
  178. if (tmp == NULL)
  179. goto invalid;
  180. tmp_cpus = tmp;
  181. }
  182. tmp_cpus[nr_cpus++].cpu = (int16_t)start_cpu;
  183. }
  184. if (*p)
  185. ++p;
  186. cpu_list = p;
  187. }
  188. if (nr_cpus > 0) {
  189. cpus = cpu_map__trim_new(nr_cpus, tmp_cpus);
  190. } else if (*cpu_list != '\0') {
  191. pr_warning("Unexpected characters at end of cpu list ('%s'), using online CPUs.",
  192. cpu_list);
  193. cpus = perf_cpu_map__new_online_cpus();
  194. } else {
  195. cpus = perf_cpu_map__new_any_cpu();
  196. }
  197. invalid:
  198. free(tmp_cpus);
  199. out:
  200. return cpus;
  201. }
  202. struct perf_cpu_map *perf_cpu_map__new_int(int cpu)
  203. {
  204. struct perf_cpu_map *cpus = perf_cpu_map__alloc(1);
  205. if (cpus)
  206. RC_CHK_ACCESS(cpus)->map[0].cpu = cpu;
  207. return cpus;
  208. }
  209. static int __perf_cpu_map__nr(const struct perf_cpu_map *cpus)
  210. {
  211. return RC_CHK_ACCESS(cpus)->nr;
  212. }
  213. struct perf_cpu perf_cpu_map__cpu(const struct perf_cpu_map *cpus, int idx)
  214. {
  215. struct perf_cpu result = {
  216. .cpu = -1
  217. };
  218. if (cpus && idx < __perf_cpu_map__nr(cpus))
  219. return __perf_cpu_map__cpu(cpus, idx);
  220. return result;
  221. }
  222. int perf_cpu_map__nr(const struct perf_cpu_map *cpus)
  223. {
  224. return cpus ? __perf_cpu_map__nr(cpus) : 1;
  225. }
  226. bool perf_cpu_map__has_any_cpu_or_is_empty(const struct perf_cpu_map *map)
  227. {
  228. return map ? __perf_cpu_map__cpu(map, 0).cpu == -1 : true;
  229. }
  230. bool perf_cpu_map__is_any_cpu_or_is_empty(const struct perf_cpu_map *map)
  231. {
  232. if (!map)
  233. return true;
  234. return __perf_cpu_map__nr(map) == 1 && __perf_cpu_map__cpu(map, 0).cpu == -1;
  235. }
  236. bool perf_cpu_map__is_empty(const struct perf_cpu_map *map)
  237. {
  238. return map == NULL;
  239. }
  240. int perf_cpu_map__idx(const struct perf_cpu_map *cpus, struct perf_cpu cpu)
  241. {
  242. int low, high;
  243. if (!cpus)
  244. return -1;
  245. low = 0;
  246. high = __perf_cpu_map__nr(cpus);
  247. while (low < high) {
  248. int idx = (low + high) / 2;
  249. struct perf_cpu cpu_at_idx = __perf_cpu_map__cpu(cpus, idx);
  250. if (cpu_at_idx.cpu == cpu.cpu)
  251. return idx;
  252. if (cpu_at_idx.cpu > cpu.cpu)
  253. high = idx;
  254. else
  255. low = idx + 1;
  256. }
  257. return -1;
  258. }
  259. bool perf_cpu_map__has(const struct perf_cpu_map *cpus, struct perf_cpu cpu)
  260. {
  261. return perf_cpu_map__idx(cpus, cpu) != -1;
  262. }
  263. bool perf_cpu_map__equal(const struct perf_cpu_map *lhs, const struct perf_cpu_map *rhs)
  264. {
  265. int nr;
  266. if (lhs == rhs)
  267. return true;
  268. if (!lhs || !rhs)
  269. return false;
  270. nr = __perf_cpu_map__nr(lhs);
  271. if (nr != __perf_cpu_map__nr(rhs))
  272. return false;
  273. for (int idx = 0; idx < nr; idx++) {
  274. if (__perf_cpu_map__cpu(lhs, idx).cpu != __perf_cpu_map__cpu(rhs, idx).cpu)
  275. return false;
  276. }
  277. return true;
  278. }
  279. bool perf_cpu_map__has_any_cpu(const struct perf_cpu_map *map)
  280. {
  281. return map && __perf_cpu_map__cpu(map, 0).cpu == -1;
  282. }
  283. struct perf_cpu perf_cpu_map__min(const struct perf_cpu_map *map)
  284. {
  285. struct perf_cpu cpu, result = {
  286. .cpu = -1
  287. };
  288. int idx;
  289. perf_cpu_map__for_each_cpu_skip_any(cpu, idx, map) {
  290. result = cpu;
  291. break;
  292. }
  293. return result;
  294. }
  295. struct perf_cpu perf_cpu_map__max(const struct perf_cpu_map *map)
  296. {
  297. struct perf_cpu result = {
  298. .cpu = -1
  299. };
  300. if (!map)
  301. return result;
  302. // The CPUs are always sorted and nr is always > 0 as 0 length map is
  303. // encoded as NULL.
  304. return __perf_cpu_map__cpu(map, __perf_cpu_map__nr(map) - 1);
  305. }
  306. /** Is 'b' a subset of 'a'. */
  307. bool perf_cpu_map__is_subset(const struct perf_cpu_map *a, const struct perf_cpu_map *b)
  308. {
  309. if (a == b || !b)
  310. return true;
  311. if (!a || __perf_cpu_map__nr(b) > __perf_cpu_map__nr(a))
  312. return false;
  313. for (int i = 0, j = 0; i < __perf_cpu_map__nr(a); i++) {
  314. if (__perf_cpu_map__cpu(a, i).cpu > __perf_cpu_map__cpu(b, j).cpu)
  315. return false;
  316. if (__perf_cpu_map__cpu(a, i).cpu == __perf_cpu_map__cpu(b, j).cpu) {
  317. j++;
  318. if (j == __perf_cpu_map__nr(b))
  319. return true;
  320. }
  321. }
  322. return false;
  323. }
  324. /*
  325. * Merge two cpumaps.
  326. *
  327. * If 'other' is subset of '*orig', '*orig' keeps itself with no reference count
  328. * change (similar to "realloc").
  329. *
  330. * If '*orig' is subset of 'other', '*orig' reuses 'other' with its reference
  331. * count increased.
  332. *
  333. * Otherwise, '*orig' gets freed and replaced with a new map.
  334. */
  335. int perf_cpu_map__merge(struct perf_cpu_map **orig, struct perf_cpu_map *other)
  336. {
  337. struct perf_cpu *tmp_cpus;
  338. int tmp_len;
  339. int i, j, k;
  340. struct perf_cpu_map *merged;
  341. if (perf_cpu_map__is_subset(*orig, other))
  342. return 0;
  343. if (perf_cpu_map__is_subset(other, *orig)) {
  344. perf_cpu_map__put(*orig);
  345. *orig = perf_cpu_map__get(other);
  346. return 0;
  347. }
  348. tmp_len = __perf_cpu_map__nr(*orig) + __perf_cpu_map__nr(other);
  349. tmp_cpus = malloc(tmp_len * sizeof(struct perf_cpu));
  350. if (!tmp_cpus)
  351. return -ENOMEM;
  352. /* Standard merge algorithm from wikipedia */
  353. i = j = k = 0;
  354. while (i < __perf_cpu_map__nr(*orig) && j < __perf_cpu_map__nr(other)) {
  355. if (__perf_cpu_map__cpu(*orig, i).cpu <= __perf_cpu_map__cpu(other, j).cpu) {
  356. if (__perf_cpu_map__cpu(*orig, i).cpu == __perf_cpu_map__cpu(other, j).cpu)
  357. j++;
  358. tmp_cpus[k++] = __perf_cpu_map__cpu(*orig, i++);
  359. } else
  360. tmp_cpus[k++] = __perf_cpu_map__cpu(other, j++);
  361. }
  362. while (i < __perf_cpu_map__nr(*orig))
  363. tmp_cpus[k++] = __perf_cpu_map__cpu(*orig, i++);
  364. while (j < __perf_cpu_map__nr(other))
  365. tmp_cpus[k++] = __perf_cpu_map__cpu(other, j++);
  366. assert(k <= tmp_len);
  367. merged = cpu_map__trim_new(k, tmp_cpus);
  368. free(tmp_cpus);
  369. perf_cpu_map__put(*orig);
  370. *orig = merged;
  371. return 0;
  372. }
  373. struct perf_cpu_map *perf_cpu_map__intersect(struct perf_cpu_map *orig,
  374. struct perf_cpu_map *other)
  375. {
  376. int i, j, k;
  377. struct perf_cpu_map *merged;
  378. if (perf_cpu_map__is_subset(other, orig))
  379. return perf_cpu_map__get(orig);
  380. if (perf_cpu_map__is_subset(orig, other))
  381. return perf_cpu_map__get(other);
  382. i = j = k = 0;
  383. while (i < __perf_cpu_map__nr(orig) && j < __perf_cpu_map__nr(other)) {
  384. if (__perf_cpu_map__cpu(orig, i).cpu < __perf_cpu_map__cpu(other, j).cpu)
  385. i++;
  386. else if (__perf_cpu_map__cpu(orig, i).cpu > __perf_cpu_map__cpu(other, j).cpu)
  387. j++;
  388. else { /* CPUs match. */
  389. i++;
  390. j++;
  391. k++;
  392. }
  393. }
  394. if (k == 0) /* Maps are completely disjoint. */
  395. return NULL;
  396. merged = perf_cpu_map__alloc(k);
  397. if (!merged)
  398. return NULL;
  399. /* Entries are added to merged in sorted order, so no need to sort again. */
  400. i = j = k = 0;
  401. while (i < __perf_cpu_map__nr(orig) && j < __perf_cpu_map__nr(other)) {
  402. if (__perf_cpu_map__cpu(orig, i).cpu < __perf_cpu_map__cpu(other, j).cpu)
  403. i++;
  404. else if (__perf_cpu_map__cpu(orig, i).cpu > __perf_cpu_map__cpu(other, j).cpu)
  405. j++;
  406. else {
  407. j++;
  408. RC_CHK_ACCESS(merged)->map[k++] = __perf_cpu_map__cpu(orig, i++);
  409. }
  410. }
  411. return merged;
  412. }