cpumap.c 12 KB

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