Commit e2dec488 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'perf-tools-fixes-for-v5.18-2022-04-14' of...

Merge tag 'perf-tools-fixes-for-v5.18-2022-04-14' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux

Pull perf tools fixes from Arnaldo Carvalho de Melo:

 - 'perf record --per-thread' mode doesn't have the CPU mask setup, so
   it can use it to figure out the number of mmaps, fix it.

 - Fix segfault accessing sample_id xyarray out of bounds, noticed while
   using Intel PT where we have a dummy event to capture text poke perf
   metadata events and we mixup the set of CPUs specified by the user
   with the all CPUs map needed for text poke.

 - Fix 'perf bench numa' to check if CPU used to bind task is online.

 - Fix 'perf bench numa' usage of affinity for machines with more than
   1000 CPUs.

 - Fix misleading add event PMU debug message, noticed while using the
  'intel_pt' PMU.

 - Fix error check return value of hashmap__new() in 'perf stat', it
   must use IS_ERR().

* tag 'perf-tools-fixes-for-v5.18-2022-04-14' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux:
  perf bench: Fix numa bench to fix usage of affinity for machines with #CPUs > 1K
  perf bench: Fix numa testcase to check if CPU used to bind task is online
  perf record: Fix per-thread option
  perf tools: Fix segfault accessing sample_id xyarray
  perf stat: Fix error check return value of hashmap__new(), must use IS_ERR()
  perf tools: Fix misleading add event PMU debug message
parents 028192fe f58faed7
......@@ -577,7 +577,6 @@ int perf_evlist__mmap_ops(struct perf_evlist *evlist,
{
struct perf_evsel *evsel;
const struct perf_cpu_map *cpus = evlist->user_requested_cpus;
const struct perf_thread_map *threads = evlist->threads;
if (!ops || !ops->get || !ops->mmap)
return -EINVAL;
......@@ -589,7 +588,7 @@ int perf_evlist__mmap_ops(struct perf_evlist *evlist,
perf_evlist__for_each_entry(evlist, evsel) {
if ((evsel->attr.read_format & PERF_FORMAT_ID) &&
evsel->sample_id == NULL &&
perf_evsel__alloc_id(evsel, perf_cpu_map__nr(cpus), threads->nr) < 0)
perf_evsel__alloc_id(evsel, evsel->fd->max_x, evsel->fd->max_y) < 0)
return -ENOMEM;
}
......
......@@ -34,6 +34,7 @@
#include <linux/numa.h>
#include <linux/zalloc.h>
#include "../util/header.h"
#include <numa.h>
#include <numaif.h>
......@@ -54,7 +55,7 @@
struct thread_data {
int curr_cpu;
cpu_set_t bind_cpumask;
cpu_set_t *bind_cpumask;
int bind_node;
u8 *process_data;
int process_nr;
......@@ -266,71 +267,115 @@ static bool node_has_cpus(int node)
return ret;
}
static cpu_set_t bind_to_cpu(int target_cpu)
static cpu_set_t *bind_to_cpu(int target_cpu)
{
cpu_set_t orig_mask, mask;
int ret;
int nrcpus = numa_num_possible_cpus();
cpu_set_t *orig_mask, *mask;
size_t size;
ret = sched_getaffinity(0, sizeof(orig_mask), &orig_mask);
BUG_ON(ret);
orig_mask = CPU_ALLOC(nrcpus);
BUG_ON(!orig_mask);
size = CPU_ALLOC_SIZE(nrcpus);
CPU_ZERO_S(size, orig_mask);
if (sched_getaffinity(0, size, orig_mask))
goto err_out;
mask = CPU_ALLOC(nrcpus);
if (!mask)
goto err_out;
CPU_ZERO(&mask);
CPU_ZERO_S(size, mask);
if (target_cpu == -1) {
int cpu;
for (cpu = 0; cpu < g->p.nr_cpus; cpu++)
CPU_SET(cpu, &mask);
CPU_SET_S(cpu, size, mask);
} else {
BUG_ON(target_cpu < 0 || target_cpu >= g->p.nr_cpus);
CPU_SET(target_cpu, &mask);
if (target_cpu < 0 || target_cpu >= g->p.nr_cpus)
goto err;
CPU_SET_S(target_cpu, size, mask);
}
ret = sched_setaffinity(0, sizeof(mask), &mask);
BUG_ON(ret);
if (sched_setaffinity(0, size, mask))
goto err;
return orig_mask;
err:
CPU_FREE(mask);
err_out:
CPU_FREE(orig_mask);
/* BUG_ON due to failure in allocation of orig_mask/mask */
BUG_ON(-1);
}
static cpu_set_t bind_to_node(int target_node)
static cpu_set_t *bind_to_node(int target_node)
{
cpu_set_t orig_mask, mask;
int nrcpus = numa_num_possible_cpus();
size_t size;
cpu_set_t *orig_mask, *mask;
int cpu;
int ret;
ret = sched_getaffinity(0, sizeof(orig_mask), &orig_mask);
BUG_ON(ret);
orig_mask = CPU_ALLOC(nrcpus);
BUG_ON(!orig_mask);
size = CPU_ALLOC_SIZE(nrcpus);
CPU_ZERO_S(size, orig_mask);
CPU_ZERO(&mask);
if (sched_getaffinity(0, size, orig_mask))
goto err_out;
mask = CPU_ALLOC(nrcpus);
if (!mask)
goto err_out;
CPU_ZERO_S(size, mask);
if (target_node == NUMA_NO_NODE) {
for (cpu = 0; cpu < g->p.nr_cpus; cpu++)
CPU_SET(cpu, &mask);
CPU_SET_S(cpu, size, mask);
} else {
struct bitmask *cpumask = numa_allocate_cpumask();
BUG_ON(!cpumask);
if (!cpumask)
goto err;
if (!numa_node_to_cpus(target_node, cpumask)) {
for (cpu = 0; cpu < (int)cpumask->size; cpu++) {
if (numa_bitmask_isbitset(cpumask, cpu))
CPU_SET(cpu, &mask);
CPU_SET_S(cpu, size, mask);
}
}
numa_free_cpumask(cpumask);
}
ret = sched_setaffinity(0, sizeof(mask), &mask);
BUG_ON(ret);
if (sched_setaffinity(0, size, mask))
goto err;
return orig_mask;
err:
CPU_FREE(mask);
err_out:
CPU_FREE(orig_mask);
/* BUG_ON due to failure in allocation of orig_mask/mask */
BUG_ON(-1);
}
static void bind_to_cpumask(cpu_set_t mask)
static void bind_to_cpumask(cpu_set_t *mask)
{
int ret;
size_t size = CPU_ALLOC_SIZE(numa_num_possible_cpus());
ret = sched_setaffinity(0, sizeof(mask), &mask);
BUG_ON(ret);
ret = sched_setaffinity(0, size, mask);
if (ret) {
CPU_FREE(mask);
BUG_ON(ret);
}
}
static void mempol_restore(void)
......@@ -376,7 +421,7 @@ do { \
static u8 *alloc_data(ssize_t bytes0, int map_flags,
int init_zero, int init_cpu0, int thp, int init_random)
{
cpu_set_t orig_mask;
cpu_set_t *orig_mask = NULL;
ssize_t bytes;
u8 *buf;
int ret;
......@@ -434,6 +479,7 @@ static u8 *alloc_data(ssize_t bytes0, int map_flags,
/* Restore affinity: */
if (init_cpu0) {
bind_to_cpumask(orig_mask);
CPU_FREE(orig_mask);
mempol_restore();
}
......@@ -585,10 +631,16 @@ static int parse_setup_cpu_list(void)
return -1;
}
if (is_cpu_online(bind_cpu_0) != 1 || is_cpu_online(bind_cpu_1) != 1) {
printf("\nTest not applicable, bind_cpu_0 or bind_cpu_1 is offline\n");
return -1;
}
BUG_ON(bind_cpu_0 < 0 || bind_cpu_1 < 0);
BUG_ON(bind_cpu_0 > bind_cpu_1);
for (bind_cpu = bind_cpu_0; bind_cpu <= bind_cpu_1; bind_cpu += step) {
size_t size = CPU_ALLOC_SIZE(g->p.nr_cpus);
int i;
for (i = 0; i < mul; i++) {
......@@ -608,10 +660,15 @@ static int parse_setup_cpu_list(void)
tprintf("%2d", bind_cpu);
}
CPU_ZERO(&td->bind_cpumask);
td->bind_cpumask = CPU_ALLOC(g->p.nr_cpus);
BUG_ON(!td->bind_cpumask);
CPU_ZERO_S(size, td->bind_cpumask);
for (cpu = bind_cpu; cpu < bind_cpu+bind_len; cpu++) {
BUG_ON(cpu < 0 || cpu >= g->p.nr_cpus);
CPU_SET(cpu, &td->bind_cpumask);
if (cpu < 0 || cpu >= g->p.nr_cpus) {
CPU_FREE(td->bind_cpumask);
BUG_ON(-1);
}
CPU_SET_S(cpu, size, td->bind_cpumask);
}
t++;
}
......@@ -752,8 +809,6 @@ static int parse_nodes_opt(const struct option *opt __maybe_unused,
return parse_node_list(arg);
}
#define BIT(x) (1ul << x)
static inline uint32_t lfsr_32(uint32_t lfsr)
{
const uint32_t taps = BIT(1) | BIT(5) | BIT(6) | BIT(31);
......@@ -1241,7 +1296,7 @@ static void *worker_thread(void *__tdata)
* by migrating to CPU#0:
*/
if (first_task && g->p.perturb_secs && (int)(stop.tv_sec - last_perturbance) >= g->p.perturb_secs) {
cpu_set_t orig_mask;
cpu_set_t *orig_mask;
int target_cpu;
int this_cpu;
......@@ -1265,6 +1320,7 @@ static void *worker_thread(void *__tdata)
printf(" (injecting perturbalance, moved to CPU#%d)\n", target_cpu);
bind_to_cpumask(orig_mask);
CPU_FREE(orig_mask);
}
if (details >= 3) {
......@@ -1398,21 +1454,31 @@ static void init_thread_data(void)
for (t = 0; t < g->p.nr_tasks; t++) {
struct thread_data *td = g->threads + t;
size_t cpuset_size = CPU_ALLOC_SIZE(g->p.nr_cpus);
int cpu;
/* Allow all nodes by default: */
td->bind_node = NUMA_NO_NODE;
/* Allow all CPUs by default: */
CPU_ZERO(&td->bind_cpumask);
td->bind_cpumask = CPU_ALLOC(g->p.nr_cpus);
BUG_ON(!td->bind_cpumask);
CPU_ZERO_S(cpuset_size, td->bind_cpumask);
for (cpu = 0; cpu < g->p.nr_cpus; cpu++)
CPU_SET(cpu, &td->bind_cpumask);
CPU_SET_S(cpu, cpuset_size, td->bind_cpumask);
}
}
static void deinit_thread_data(void)
{
ssize_t size = sizeof(*g->threads)*g->p.nr_tasks;
int t;
/* Free the bind_cpumask allocated for thread_data */
for (t = 0; t < g->p.nr_tasks; t++) {
struct thread_data *td = g->threads + t;
CPU_FREE(td->bind_cpumask);
}
free_data(g->threads, size);
}
......
......@@ -989,8 +989,11 @@ static int record__thread_data_init_maps(struct record_thread *thread_data, stru
struct mmap *overwrite_mmap = evlist->overwrite_mmap;
struct perf_cpu_map *cpus = evlist->core.user_requested_cpus;
thread_data->nr_mmaps = bitmap_weight(thread_data->mask->maps.bits,
thread_data->mask->maps.nbits);
if (cpu_map__is_dummy(cpus))
thread_data->nr_mmaps = nr_mmaps;
else
thread_data->nr_mmaps = bitmap_weight(thread_data->mask->maps.bits,
thread_data->mask->maps.nbits);
if (mmap) {
thread_data->maps = zalloc(thread_data->nr_mmaps * sizeof(struct mmap *));
if (!thread_data->maps)
......@@ -1007,16 +1010,17 @@ static int record__thread_data_init_maps(struct record_thread *thread_data, stru
thread_data->nr_mmaps, thread_data->maps, thread_data->overwrite_maps);
for (m = 0, tm = 0; m < nr_mmaps && tm < thread_data->nr_mmaps; m++) {
if (test_bit(cpus->map[m].cpu, thread_data->mask->maps.bits)) {
if (cpu_map__is_dummy(cpus) ||
test_bit(cpus->map[m].cpu, thread_data->mask->maps.bits)) {
if (thread_data->maps) {
thread_data->maps[tm] = &mmap[m];
pr_debug2("thread_data[%p]: cpu%d: maps[%d] -> mmap[%d]\n",
thread_data, cpus->map[m].cpu, tm, m);
thread_data, perf_cpu_map__cpu(cpus, m).cpu, tm, m);
}
if (thread_data->overwrite_maps) {
thread_data->overwrite_maps[tm] = &overwrite_mmap[m];
pr_debug2("thread_data[%p]: cpu%d: ow_maps[%d] -> ow_mmap[%d]\n",
thread_data, cpus->map[m].cpu, tm, m);
thread_data, perf_cpu_map__cpu(cpus, m).cpu, tm, m);
}
tm++;
}
......@@ -3329,6 +3333,9 @@ static void record__mmap_cpu_mask_init(struct mmap_cpu_mask *mask, struct perf_c
{
int c;
if (cpu_map__is_dummy(cpus))
return;
for (c = 0; c < cpus->nr; c++)
set_bit(cpus->map[c].cpu, mask->bits);
}
......@@ -3680,6 +3687,11 @@ static int record__init_thread_masks(struct record *rec)
if (!record__threads_enabled(rec))
return record__init_thread_default_masks(rec, cpus);
if (cpu_map__is_dummy(cpus)) {
pr_err("--per-thread option is mutually exclusive to parallel streaming mode.\n");
return -EINVAL;
}
switch (rec->opts.threads_spec) {
case THREAD_SPEC__CPU:
ret = record__init_thread_cpu_masks(rec, cpus);
......
......@@ -983,6 +983,57 @@ static int write_dir_format(struct feat_fd *ff,
return do_write(ff, &data->dir.version, sizeof(data->dir.version));
}
/*
* Check whether a CPU is online
*
* Returns:
* 1 -> if CPU is online
* 0 -> if CPU is offline
* -1 -> error case
*/
int is_cpu_online(unsigned int cpu)
{
char *str;
size_t strlen;
char buf[256];
int status = -1;
struct stat statbuf;
snprintf(buf, sizeof(buf),
"/sys/devices/system/cpu/cpu%d", cpu);
if (stat(buf, &statbuf) != 0)
return 0;
/*
* Check if /sys/devices/system/cpu/cpux/online file
* exists. Some cases cpu0 won't have online file since
* it is not expected to be turned off generally.
* In kernels without CONFIG_HOTPLUG_CPU, this
* file won't exist
*/
snprintf(buf, sizeof(buf),
"/sys/devices/system/cpu/cpu%d/online", cpu);
if (stat(buf, &statbuf) != 0)
return 1;
/*
* Read online file using sysfs__read_str.
* If read or open fails, return -1.
* If read succeeds, return value from file
* which gets stored in "str"
*/
snprintf(buf, sizeof(buf),
"devices/system/cpu/cpu%d/online", cpu);
if (sysfs__read_str(buf, &str, &strlen) < 0)
return status;
status = atoi(str);
free(str);
return status;
}
#ifdef HAVE_LIBBPF_SUPPORT
static int write_bpf_prog_info(struct feat_fd *ff,
struct evlist *evlist __maybe_unused)
......
......@@ -158,6 +158,7 @@ int do_write(struct feat_fd *fd, const void *buf, size_t size);
int write_padded(struct feat_fd *fd, const void *bf,
size_t count, size_t count_aligned);
int is_cpu_online(unsigned int cpu);
/*
* arch specific callback
*/
......
......@@ -1523,7 +1523,9 @@ int parse_events_add_pmu(struct parse_events_state *parse_state,
bool use_uncore_alias;
LIST_HEAD(config_terms);
if (verbose > 1) {
pmu = parse_state->fake_pmu ?: perf_pmu__find(name);
if (verbose > 1 && !(pmu && pmu->selectable)) {
fprintf(stderr, "Attempting to add event pmu '%s' with '",
name);
if (head_config) {
......@@ -1536,7 +1538,6 @@ int parse_events_add_pmu(struct parse_events_state *parse_state,
fprintf(stderr, "' that may result in non-fatal errors\n");
}
pmu = parse_state->fake_pmu ?: perf_pmu__find(name);
if (!pmu) {
char *err_str;
......
// SPDX-License-Identifier: GPL-2.0
#include <errno.h>
#include <linux/err.h>
#include <inttypes.h>
#include <math.h>
#include <string.h>
......@@ -311,7 +312,7 @@ static int check_per_pkg(struct evsel *counter, struct perf_counts_values *vals,
if (!mask) {
mask = hashmap__new(pkg_id_hash, pkg_id_equal, NULL);
if (!mask)
if (IS_ERR(mask))
return -ENOMEM;
counter->per_pkg_mask = mask;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment