linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] perf: fix topology test on systems with sparse CPUs
@ 2017-01-30 16:53 Jan Stancek
  2017-01-30 18:49 ` Jiri Olsa
                   ` (2 more replies)
  0 siblings, 3 replies; 24+ messages in thread
From: Jan Stancek @ 2017-01-30 16:53 UTC (permalink / raw)
  To: linux-kernel
  Cc: peterz, mingo, acme, alexander.shishkin, jstancek, jolsa,
	mhiramat, rui.teng, sukadev

Topology test fails on systems with sparse CPUs, e.g.
CPU not present or offline:

36: Test topology in session                                 :
--- start ---
test child forked, pid 23703
templ file: /tmp/perf-test-i2rNki
failed to write feature 13
perf: Segmentation fault

available: 2 nodes (0-1)
node 0 cpus: 0 6 8 10 16 22 24 26
node 0 size: 11797 MB
node 0 free: 10526 MB
node 1 cpus: 1 7 9 11 17 23 25 27
node 1 size: 12065 MB
node 1 free: 10770 MB
node distances:
node   0   1
  0:  10  20
  1:  20  10

Enumerating CPU ids from 0 to _SC_NPROCESSORS_CONF-1 in header.env.cpu[]
doesn't work on system like one above, because some ids are higher than
number of CPUs, and there can be gaps.

On top of that, if CPU is offline, we can't get topology info from
sysfs entries, because they don't exist for offline CPUs.

This patch stores topology data only for online CPUs in header.env.cpu[]
list, regardless of their CPU ids, and then uses cpu_map to translate
index to actual CPU id.

Example:
coreid socketid for CPU0
coreid socketid for CPU1
coreid socketid for CPU6
coreid socketid for CPU7
...

Alternative is we go from 0 to highest CPU id, but CPUs which are
missing would contain some dummy values in topology data.

Example:
coreid socketid for CPU0
coreid socketid for CPU1
-1 -1
-1 -1
-1 -1
-1 -1
coreid socketid for CPU6
coreid socketid for CPU7
...

Signed-off-by: Jan Stancek <jstancek@redhat.com>
---
 tools/perf/tests/topology.c |  7 ++++---
 tools/perf/util/env.c       | 40 ++++++++++++++++++++++++++++------------
 tools/perf/util/header.c    | 36 ++++++++++++++++++++----------------
 3 files changed, 52 insertions(+), 31 deletions(-)

diff --git a/tools/perf/tests/topology.c b/tools/perf/tests/topology.c
index 98fe69ac553c..7b0b621ea8c0 100644
--- a/tools/perf/tests/topology.c
+++ b/tools/perf/tests/topology.c
@@ -66,17 +66,18 @@ static int check_cpu_topology(char *path, struct cpu_map *map)
 	TEST_ASSERT_VAL("can't get session", session);
 
 	for (i = 0; i < session->header.env.nr_cpus_online; i++) {
-		pr_debug("CPU %d, core %d, socket %d\n", i,
+		pr_debug("CPU %d, core %d, socket %d\n", map->map[i],
 			 session->header.env.cpu[i].core_id,
 			 session->header.env.cpu[i].socket_id);
 	}
 
 	for (i = 0; i < map->nr; i++) {
+		int cpu = map->map[i];
 		TEST_ASSERT_VAL("Core ID doesn't match",
-			(session->header.env.cpu[map->map[i]].core_id == (cpu_map__get_core(map, i, NULL) & 0xffff)));
+			(session->header.env.cpu[i].core_id == (cpu_map__get_core_id(cpu) & 0xffff)));
 
 		TEST_ASSERT_VAL("Socket ID doesn't match",
-			(session->header.env.cpu[map->map[i]].socket_id == cpu_map__get_socket(map, i, NULL)));
+			(session->header.env.cpu[i].socket_id == cpu_map__get_socket_id(cpu)));
 	}
 
 	perf_session__delete(session);
diff --git a/tools/perf/util/env.c b/tools/perf/util/env.c
index bb964e86b09d..0c2cae807a61 100644
--- a/tools/perf/util/env.c
+++ b/tools/perf/util/env.c
@@ -60,29 +60,45 @@ int perf_env__set_cmdline(struct perf_env *env, int argc, const char *argv[])
 
 int perf_env__read_cpu_topology_map(struct perf_env *env)
 {
-	int cpu, nr_cpus;
+	int cpu, nr_cpus, i, err = 0;
+	struct cpu_map *map;
 
 	if (env->cpu != NULL)
 		return 0;
 
-	if (env->nr_cpus_avail == 0)
-		env->nr_cpus_avail = sysconf(_SC_NPROCESSORS_CONF);
+	map = cpu_map__new(NULL);
+	if (map == NULL) {
+		pr_debug("failed to get system cpumap\n");
+		err = -ENOMEM;
+		goto out;
+	}
+
+	if (env->nr_cpus_online == 0)
+		env->nr_cpus_online = map->nr;
 
-	nr_cpus = env->nr_cpus_avail;
-	if (nr_cpus == -1)
-		return -EINVAL;
+	nr_cpus = env->nr_cpus_online;
+	if (nr_cpus == -1 || map->nr < nr_cpus) {
+		err = -EINVAL;
+		goto out_free;
+	}
 
 	env->cpu = calloc(nr_cpus, sizeof(env->cpu[0]));
-	if (env->cpu == NULL)
-		return -ENOMEM;
+	if (env->cpu == NULL) {
+		err = -ENOMEM;
+		goto out_free;
+	}
 
-	for (cpu = 0; cpu < nr_cpus; ++cpu) {
-		env->cpu[cpu].core_id	= cpu_map__get_core_id(cpu);
-		env->cpu[cpu].socket_id	= cpu_map__get_socket_id(cpu);
+	for (i = 0; i < nr_cpus; i++) {
+		cpu = map->map[i];
+		env->cpu[i].core_id	= cpu_map__get_core_id(cpu);
+		env->cpu[i].socket_id	= cpu_map__get_socket_id(cpu);
 	}
 
 	env->nr_cpus_avail = nr_cpus;
-	return 0;
+out_free:
+	cpu_map__put(map);
+out:
+	return err;
 }
 
 void cpu_cache_level__free(struct cpu_cache_level *cache)
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index d89c9c7ef4e5..25faa93d143a 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -503,41 +503,45 @@ static void free_cpu_topo(struct cpu_topo *tp)
 
 static struct cpu_topo *build_cpu_topology(void)
 {
-	struct cpu_topo *tp;
+	struct cpu_topo *tp = NULL;
 	void *addr;
-	u32 nr, i;
+	u32 i;
 	size_t sz;
-	long ncpus;
-	int ret = -1;
-
-	ncpus = sysconf(_SC_NPROCESSORS_CONF);
-	if (ncpus < 0)
-		return NULL;
-
-	nr = (u32)(ncpus & UINT_MAX);
+	int ret = 0, cpu;
+	struct cpu_map *map;
 
-	sz = nr * sizeof(char *);
+	map = cpu_map__new(NULL);
+	if (map == NULL) {
+		pr_debug("failed to get system cpumap\n");
+		goto out;
+	}
 
+	sz = map->nr * sizeof(char *);
 	addr = calloc(1, sizeof(*tp) + 2 * sz);
 	if (!addr)
-		return NULL;
+		goto out_free;
 
 	tp = addr;
-	tp->cpu_nr = nr;
+	tp->cpu_nr = map->nr;
 	addr += sizeof(*tp);
 	tp->core_siblings = addr;
 	addr += sz;
 	tp->thread_siblings = addr;
 
-	for (i = 0; i < nr; i++) {
-		ret = build_cpu_topo(tp, i);
+	for (i = 0; i < tp->cpu_nr; i++) {
+		cpu = map->map[i];
+		ret = build_cpu_topo(tp, cpu);
 		if (ret < 0)
 			break;
 	}
+
+out_free:
+	cpu_map__put(map);
 	if (ret) {
 		free_cpu_topo(tp);
 		tp = NULL;
 	}
+out:
 	return tp;
 }
 
@@ -575,7 +579,7 @@ static int write_cpu_topology(int fd, struct perf_header *h __maybe_unused,
 	if (ret < 0)
 		goto done;
 
-	for (j = 0; j < perf_env.nr_cpus_avail; j++) {
+	for (j = 0; j < perf_env.nr_cpus_online; j++) {
 		ret = do_write(fd, &perf_env.cpu[j].core_id,
 			       sizeof(perf_env.cpu[j].core_id));
 		if (ret < 0)
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* Re: [PATCH] perf: fix topology test on systems with sparse CPUs
  2017-01-30 16:53 [PATCH] perf: fix topology test on systems with sparse CPUs Jan Stancek
@ 2017-01-30 18:49 ` Jiri Olsa
  2017-01-30 19:29   ` Jan Stancek
  2017-01-31 16:03   ` Jan Stancek
  2017-01-30 18:49 ` Jiri Olsa
  2017-01-30 18:49 ` Jiri Olsa
  2 siblings, 2 replies; 24+ messages in thread
From: Jiri Olsa @ 2017-01-30 18:49 UTC (permalink / raw)
  To: Jan Stancek
  Cc: linux-kernel, peterz, mingo, acme, alexander.shishkin, jolsa,
	mhiramat, rui.teng, sukadev

On Mon, Jan 30, 2017 at 05:53:34PM +0100, Jan Stancek wrote:

SNIP

> +		ret = build_cpu_topo(tp, cpu);
>  		if (ret < 0)
>  			break;
>  	}
> +
> +out_free:
> +	cpu_map__put(map);
>  	if (ret) {
>  		free_cpu_topo(tp);
>  		tp = NULL;
>  	}
> +out:
>  	return tp;
>  }
>  
> @@ -575,7 +579,7 @@ static int write_cpu_topology(int fd, struct perf_header *h __maybe_unused,
>  	if (ret < 0)
>  		goto done;
>  
> -	for (j = 0; j < perf_env.nr_cpus_avail; j++) {
> +	for (j = 0; j < perf_env.nr_cpus_online; j++) {

so basically we're changing from avail to online cpus

have you checked all the users of this FEATURE
if such change is ok?

I can't find any after quick search, but it would be
good to be sure and mention that in changelog

thanks,
jirka

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH] perf: fix topology test on systems with sparse CPUs
  2017-01-30 16:53 [PATCH] perf: fix topology test on systems with sparse CPUs Jan Stancek
  2017-01-30 18:49 ` Jiri Olsa
@ 2017-01-30 18:49 ` Jiri Olsa
  2017-01-30 18:49 ` Jiri Olsa
  2 siblings, 0 replies; 24+ messages in thread
From: Jiri Olsa @ 2017-01-30 18:49 UTC (permalink / raw)
  To: Jan Stancek
  Cc: linux-kernel, peterz, mingo, acme, alexander.shishkin, jolsa,
	mhiramat, rui.teng, sukadev

On Mon, Jan 30, 2017 at 05:53:34PM +0100, Jan Stancek wrote:
> Topology test fails on systems with sparse CPUs, e.g.

SNIP

>  
> -	for (i = 0; i < nr; i++) {
> -		ret = build_cpu_topo(tp, i);
> +	for (i = 0; i < tp->cpu_nr; i++) {
> +		cpu = map->map[i];

no need for cpu variable

jirka

> +		ret = build_cpu_topo(tp, cpu);
>  		if (ret < 0)
>  			break;

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH] perf: fix topology test on systems with sparse CPUs
  2017-01-30 16:53 [PATCH] perf: fix topology test on systems with sparse CPUs Jan Stancek
  2017-01-30 18:49 ` Jiri Olsa
  2017-01-30 18:49 ` Jiri Olsa
@ 2017-01-30 18:49 ` Jiri Olsa
  2 siblings, 0 replies; 24+ messages in thread
From: Jiri Olsa @ 2017-01-30 18:49 UTC (permalink / raw)
  To: Jan Stancek
  Cc: linux-kernel, peterz, mingo, acme, alexander.shishkin, jolsa,
	mhiramat, rui.teng, sukadev

On Mon, Jan 30, 2017 at 05:53:34PM +0100, Jan Stancek wrote:

SNIP

> diff --git a/tools/perf/util/env.c b/tools/perf/util/env.c
> index bb964e86b09d..0c2cae807a61 100644
> --- a/tools/perf/util/env.c
> +++ b/tools/perf/util/env.c
> @@ -60,29 +60,45 @@ int perf_env__set_cmdline(struct perf_env *env, int argc, const char *argv[])
>  
>  int perf_env__read_cpu_topology_map(struct perf_env *env)
>  {
> -	int cpu, nr_cpus;
> +	int cpu, nr_cpus, i, err = 0;
> +	struct cpu_map *map;
>  
>  	if (env->cpu != NULL)
>  		return 0;
>  
> -	if (env->nr_cpus_avail == 0)
> -		env->nr_cpus_avail = sysconf(_SC_NPROCESSORS_CONF);
> +	map = cpu_map__new(NULL);

could you please put comment in here, explaining that
cpu_map__new(NULL) makes map with current online cpus

thanks,
jirka

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH] perf: fix topology test on systems with sparse CPUs
  2017-01-30 18:49 ` Jiri Olsa
@ 2017-01-30 19:29   ` Jan Stancek
  2017-01-31 16:03   ` Jan Stancek
  1 sibling, 0 replies; 24+ messages in thread
From: Jan Stancek @ 2017-01-30 19:29 UTC (permalink / raw)
  To: Jiri Olsa
  Cc: linux-kernel, peterz, mingo, acme, alexander shishkin, jolsa,
	mhiramat, rui teng, sukadev





----- Original Message -----
> From: "Jiri Olsa" <jolsa@redhat.com>
> To: "Jan Stancek" <jstancek@redhat.com>
> Cc: linux-kernel@vger.kernel.org, peterz@infradead.org, mingo@redhat.com, acme@kernel.org, "alexander shishkin"
> <alexander.shishkin@linux.intel.com>, jolsa@kernel.org, mhiramat@kernel.org, "rui teng"
> <rui.teng@linux.vnet.ibm.com>, sukadev@linux.vnet.ibm.com
> Sent: Monday, 30 January, 2017 7:49:08 PM
> Subject: Re: [PATCH] perf: fix topology test on systems with sparse CPUs
> 
> On Mon, Jan 30, 2017 at 05:53:34PM +0100, Jan Stancek wrote:
> 
> SNIP
> 
> > +		ret = build_cpu_topo(tp, cpu);
> >  		if (ret < 0)
> >  			break;
> >  	}
> > +
> > +out_free:
> > +	cpu_map__put(map);
> >  	if (ret) {
> >  		free_cpu_topo(tp);
> >  		tp = NULL;
> >  	}
> > +out:
> >  	return tp;
> >  }
> >  
> > @@ -575,7 +579,7 @@ static int write_cpu_topology(int fd, struct
> > perf_header *h __maybe_unused,
> >  	if (ret < 0)
> >  		goto done;
> >  
> > -	for (j = 0; j < perf_env.nr_cpus_avail; j++) {
> > +	for (j = 0; j < perf_env.nr_cpus_online; j++) {
> 
> so basically we're changing from avail to online cpus
> 
> have you checked all the users of this FEATURE
> if such change is ok?

You're right, I missed some. Looking again, I see at least
perf_env__get_core() could break.

Regards,
Jan

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH] perf: fix topology test on systems with sparse CPUs
  2017-01-30 18:49 ` Jiri Olsa
  2017-01-30 19:29   ` Jan Stancek
@ 2017-01-31 16:03   ` Jan Stancek
  2017-02-02 11:29     ` Jiri Olsa
  2017-02-02 11:29     ` [PATCH] perf: fix topology test on systems with sparse CPUs Jiri Olsa
  1 sibling, 2 replies; 24+ messages in thread
From: Jan Stancek @ 2017-01-31 16:03 UTC (permalink / raw)
  To: Jiri Olsa
  Cc: linux-kernel, peterz, mingo, acme, alexander.shishkin, jolsa,
	mhiramat, rui.teng, sukadev, jstancek

[-- Attachment #1: Type: text/plain, Size: 2605 bytes --]

On 01/30/2017 07:49 PM, Jiri Olsa wrote:
> so basically we're changing from avail to online cpus
> 
> have you checked all the users of this FEATURE
> if such change is ok?

Jiri,

It wasn't OK as there are other users who index cpu_topology_map by CPU id.
I decided to give the alternative a try (attached): keep cpu_topology_map
indexed by CPU id, but extend it to fit max present CPU.

So for a system like this one:
  _SC_NPROCESSORS_CONF == 16
  available: 2 nodes (0-1)
  node 0 cpus: 0 6 8 10 16 22 24 26
  node 0 size: 12004 MB
  node 0 free: 9470 MB
  node 1 cpus: 1 7 9 11 23 25 27
  node 1 size: 12093 MB
  node 1 free: 9406 MB
  node distances:
  node   0   1
    0:  10  20
    1:  20  10

HEADER_NRCPUS before:
nr_cpus_online = 15
nr_cpus_available = 16

HEADER_CPU_TOPOLOGY before:
core_sib_nr: 2
core_siblings: 0,6,8,10,16,22,24,26
core_siblings: 1,7,9,11,23,25,27
thread_sib_nr: 8
thread_siblings: 0,16
thread_siblings: 1
thread_siblings: 6,22
thread_siblings: 7,23
thread_siblings: 8,24
thread_siblings: 9,25
thread_siblings: 10,26
thread_siblings: 11,27
core_id: 0, socket_id: 0
core_id: 0, socket_id: 1
core_id: -1, socket_id: -1
core_id: -1, socket_id: -1
core_id: -1, socket_id: -1
core_id: -1, socket_id: -1
core_id: 10, socket_id: 0
core_id: 10, socket_id: 1
core_id: 1, socket_id: 0
core_id: 1, socket_id: 1
core_id: 9, socket_id: 0
core_id: 9, socket_id: 1
core_id: -1, socket_id: -1
core_id: -1, socket_id: -1
core_id: -1, socket_id: -1
core_id: -1, socket_id: -1

HEADER_NRCPUS after:
nr_cpus_online = 15
nr_cpus_available = 28

HEADER_CPU_TOPOLOGY after:
core_sib_nr: 2
core_siblings: 0,6,8,10,16,22,24,26
core_siblings: 1,7,9,11,23,25,27
thread_sib_nr: 8
thread_siblings: 0,16
thread_siblings: 1
thread_siblings: 6,22
thread_siblings: 7,23
thread_siblings: 8,24
thread_siblings: 9,25
thread_siblings: 10,26
thread_siblings: 11,27
core_id: 0, socket_id: 0
core_id: 0, socket_id: 1
core_id: -1, socket_id: -1
core_id: -1, socket_id: -1
core_id: -1, socket_id: -1
core_id: -1, socket_id: -1
core_id: 10, socket_id: 0
core_id: 10, socket_id: 1
core_id: 1, socket_id: 0
core_id: 1, socket_id: 1
core_id: 9, socket_id: 0
core_id: 9, socket_id: 1
core_id: -1, socket_id: -1
core_id: -1, socket_id: -1
core_id: -1, socket_id: -1
core_id: -1, socket_id: -1
core_id: 0, socket_id: 0
core_id: -1, socket_id: -1
core_id: -1, socket_id: -1
core_id: -1, socket_id: -1
core_id: -1, socket_id: -1
core_id: -1, socket_id: -1
core_id: 10, socket_id: 0
core_id: 10, socket_id: 1
core_id: 1, socket_id: 0
core_id: 1, socket_id: 1
core_id: 9, socket_id: 0
core_id: 9, socket_id: 1

Regards,
Jan


[-- Attachment #2: 0001-perf-add-cpu__max_present_cpu.patch --]
[-- Type: text/x-patch, Size: 2214 bytes --]

>From 9bf8ece1e397b851beedaeceeb0cd07421ff6f43 Mon Sep 17 00:00:00 2001
Message-Id: <9bf8ece1e397b851beedaeceeb0cd07421ff6f43.1485877985.git.jstancek@redhat.com>
From: Jan Stancek <jstancek@redhat.com>
Date: Tue, 31 Jan 2017 14:41:46 +0100
Subject: [PATCH 1/3 v2] perf: add cpu__max_present_cpu()

Similar to cpu__max_cpu() (which returns max possible CPU),
returns max present CPU.

Signed-off-by: Jan Stancek <jstancek@redhat.com>
---
 tools/perf/util/cpumap.c | 22 ++++++++++++++++++++++
 tools/perf/util/cpumap.h |  1 +
 2 files changed, 23 insertions(+)

diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c
index 2c0b52264a46..8c7504939113 100644
--- a/tools/perf/util/cpumap.c
+++ b/tools/perf/util/cpumap.c
@@ -9,6 +9,7 @@
 #include "asm/bug.h"
 
 static int max_cpu_num;
+static int max_present_cpu_num;
 static int max_node_num;
 static int *cpunode_map;
 
@@ -442,6 +443,7 @@ static void set_max_cpu_num(void)
 
 	/* set up default */
 	max_cpu_num = 4096;
+	max_present_cpu_num = 4096;
 
 	mnt = sysfs__mountpoint();
 	if (!mnt)
@@ -455,6 +457,17 @@ static void set_max_cpu_num(void)
 	}
 
 	ret = get_max_num(path, &max_cpu_num);
+	if (ret)
+		goto out;
+
+	/* get the highest present cpu number for a sparse allocation */
+	ret = snprintf(path, PATH_MAX, "%s/devices/system/cpu/present", mnt);
+	if (ret == PATH_MAX) {
+		pr_err("sysfs path crossed PATH_MAX(%d) size\n", PATH_MAX);
+		goto out;
+	}
+
+	ret = get_max_num(path, &max_present_cpu_num);
 
 out:
 	if (ret)
@@ -505,6 +518,15 @@ int cpu__max_cpu(void)
 	return max_cpu_num;
 }
 
+int cpu__max_present_cpu(void)
+{
+	if (unlikely(!max_present_cpu_num))
+		set_max_cpu_num();
+
+	return max_present_cpu_num;
+}
+
+
 int cpu__get_node(int cpu)
 {
 	if (unlikely(cpunode_map == NULL)) {
diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h
index 06bd689f5989..1a0549af8f5c 100644
--- a/tools/perf/util/cpumap.h
+++ b/tools/perf/util/cpumap.h
@@ -62,6 +62,7 @@ static inline bool cpu_map__empty(const struct cpu_map *map)
 
 int cpu__max_node(void);
 int cpu__max_cpu(void);
+int cpu__max_present_cpu(void);
 int cpu__get_node(int cpu);
 
 int cpu_map__build_map(struct cpu_map *cpus, struct cpu_map **res,
-- 
1.8.3.1


[-- Attachment #3: 0002-perf-make-build_cpu_topology-skip-offline-absent-CPU.patch --]
[-- Type: text/x-patch, Size: 3055 bytes --]

>From 8f23543a314f359ee0625345bbc6d54b0e278358 Mon Sep 17 00:00:00 2001
Message-Id: <8f23543a314f359ee0625345bbc6d54b0e278358.1485877985.git.jstancek@redhat.com>
In-Reply-To: <9bf8ece1e397b851beedaeceeb0cd07421ff6f43.1485877985.git.jstancek@redhat.com>
References: <9bf8ece1e397b851beedaeceeb0cd07421ff6f43.1485877985.git.jstancek@redhat.com>
From: Jan Stancek <jstancek@redhat.com>
Date: Tue, 31 Jan 2017 14:44:57 +0100
Subject: [PATCH 2/3 v2] perf: make build_cpu_topology skip offline/absent CPUs

When build_cpu_topo() encounters offline/absent CPUs,
it fails to find any sysfs entries and returns failure.
This leads to build_cpu_topology() and write_cpu_topology()
failing as well.

Because HEADER_CPU_TOPOLOGY has not been written, read leaves
cpu_topology_map NULL and we get NULL ptr deref at:

 ...
  cmd_test
   __cmd_test
    test_and_print
     run_test
      test_session_topology
       check_cpu_topology

36: Session topology                           :
--- start ---
test child forked, pid 14902
templ file: /tmp/perf-test-4CKocW
failed to write feature HEADER_CPU_TOPOLOGY
perf: Segmentation fault
Obtained 9 stack frames.
./perf(sighandler_dump_stack+0x41) [0x5095f1]
/lib64/libc.so.6(+0x35250) [0x7f4b7c3c9250]
./perf(test_session_topology+0x1db) [0x490ceb]
./perf() [0x475b68]
./perf(cmd_test+0x5b9) [0x4763c9]
./perf() [0x4945a3]
./perf(main+0x69f) [0x427e8f]
/lib64/libc.so.6(__libc_start_main+0xf5) [0x7f4b7c3b5b35]
./perf() [0x427fb9]
test child interrupted
---- end ----
Session topology: FAILED!

This patch makes build_cpu_topology() skip offline/absent CPUs,
by checking their presence against cpu_map built from online CPUs.

Signed-off-by: Jan Stancek <jstancek@redhat.com>
---
 tools/perf/util/header.c | 24 +++++++++++++++++++-----
 1 file changed, 19 insertions(+), 5 deletions(-)

diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index d89c9c7ef4e5..2b7ed52df807 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -503,24 +503,31 @@ static void free_cpu_topo(struct cpu_topo *tp)
 
 static struct cpu_topo *build_cpu_topology(void)
 {
-	struct cpu_topo *tp;
+	struct cpu_topo *tp = NULL;
 	void *addr;
 	u32 nr, i;
 	size_t sz;
 	long ncpus;
-	int ret = -1;
+	int ret = 0;
+	struct cpu_map *map;
 
 	ncpus = sysconf(_SC_NPROCESSORS_CONF);
 	if (ncpus < 0)
-		return NULL;
+		goto out;
+
+	/* build online CPU map */
+	map = cpu_map__new(NULL);
+	if (map == NULL) {
+		pr_debug("failed to get system cpumap\n");
+		goto out;
+	}
 
 	nr = (u32)(ncpus & UINT_MAX);
 
 	sz = nr * sizeof(char *);
-
 	addr = calloc(1, sizeof(*tp) + 2 * sz);
 	if (!addr)
-		return NULL;
+		goto out_free;
 
 	tp = addr;
 	tp->cpu_nr = nr;
@@ -530,14 +537,21 @@ static struct cpu_topo *build_cpu_topology(void)
 	tp->thread_siblings = addr;
 
 	for (i = 0; i < nr; i++) {
+		if (!cpu_map__has(map, i))
+			continue;
+
 		ret = build_cpu_topo(tp, i);
 		if (ret < 0)
 			break;
 	}
+
+out_free:
+	cpu_map__put(map);
 	if (ret) {
 		free_cpu_topo(tp);
 		tp = NULL;
 	}
+out:
 	return tp;
 }
 
-- 
1.8.3.1


[-- Attachment #4: 0003-perf-replace-_SC_NPROCESSORS_CONF-with-max_present_c.patch --]
[-- Type: text/x-patch, Size: 5783 bytes --]

>From 4a43c50e547b4a0bc474d56fe3f07d137774d60a Mon Sep 17 00:00:00 2001
Message-Id: <4a43c50e547b4a0bc474d56fe3f07d137774d60a.1485877985.git.jstancek@redhat.com>
In-Reply-To: <9bf8ece1e397b851beedaeceeb0cd07421ff6f43.1485877985.git.jstancek@redhat.com>
References: <9bf8ece1e397b851beedaeceeb0cd07421ff6f43.1485877985.git.jstancek@redhat.com>
From: Jan Stancek <jstancek@redhat.com>
Date: Tue, 31 Jan 2017 14:46:54 +0100
Subject: [PATCH 3/3 v2] perf: replace _SC_NPROCESSORS_CONF with max_present_cpu
 in cpu_topology_map

There are 2 problems wrt. cpu_topology_map on systems with sparse CPUs:

1. offline/absent CPUs will have their socket_id and core_id set to -1
   which triggers:
   "socket_id number is too big.You may need to upgrade the perf tool."

2. size of cpu_topology_map (perf_env.cpu[]) is allocated based on
   _SC_NPROCESSORS_CONF, but can be indexed with CPU ids going above.
   Users of perf_env.cpu[] are using CPU id as index. This can lead
   to read beyond what was allocated:
   ==19991== Invalid read of size 4
   ==19991==    at 0x490CEB: check_cpu_topology (topology.c:69)
   ==19991==    by 0x490CEB: test_session_topology (topology.c:106)
   ...

For example:
  _SC_NPROCESSORS_CONF == 16
  available: 2 nodes (0-1)
  node 0 cpus: 0 6 8 10 16 22 24 26
  node 0 size: 12004 MB
  node 0 free: 9470 MB
  node 1 cpus: 1 7 9 11 23 25 27
  node 1 size: 12093 MB
  node 1 free: 9406 MB
  node distances:
  node   0   1
    0:  10  20
    1:  20  10

This patch changes HEADER_NRCPUS.nr_cpus_available from _SC_NPROCESSORS_CONF
to max_present_cpu and updates any user of cpu_topology_map to iterate
with nr_cpus_avail. As consequence HEADER_CPU_TOPOLOGY core_id and
socket_id lists get longer, but maintain compatibility with pre-patch
state - index to cpu_topology_map is CPU id.

 ./perf test 36 -v
36: Session topology                           :
--- start ---
test child forked, pid 22211
templ file: /tmp/perf-test-gmdX5i
CPU 0, core 0, socket 0
CPU 1, core 0, socket 1
CPU 6, core 10, socket 0
CPU 7, core 10, socket 1
CPU 8, core 1, socket 0
CPU 9, core 1, socket 1
CPU 10, core 9, socket 0
CPU 11, core 9, socket 1
CPU 16, core 0, socket 0
CPU 22, core 10, socket 0
CPU 23, core 10, socket 1
CPU 24, core 1, socket 0
CPU 25, core 1, socket 1
CPU 26, core 9, socket 0
CPU 27, core 9, socket 1
test child finished with 0
---- end ----
Session topology: Ok

Signed-off-by: Jan Stancek <jstancek@redhat.com>
---
 tools/perf/builtin-stat.c   |  2 +-
 tools/perf/tests/topology.c |  4 +++-
 tools/perf/util/env.c       |  2 +-
 tools/perf/util/header.c    | 16 +++++-----------
 4 files changed, 10 insertions(+), 14 deletions(-)

diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c
index a02f2e965628..9e2be1c52fbd 100644
--- a/tools/perf/builtin-stat.c
+++ b/tools/perf/builtin-stat.c
@@ -1765,7 +1765,7 @@ static inline int perf_env__get_cpu(struct perf_env *env, struct cpu_map *map, i
 
 	cpu = map->map[idx];
 
-	if (cpu >= env->nr_cpus_online)
+	if (cpu >= env->nr_cpus_avail)
 		return -1;
 
 	return cpu;
diff --git a/tools/perf/tests/topology.c b/tools/perf/tests/topology.c
index 98fe69ac553c..803f893550d6 100644
--- a/tools/perf/tests/topology.c
+++ b/tools/perf/tests/topology.c
@@ -65,7 +65,9 @@ static int check_cpu_topology(char *path, struct cpu_map *map)
 	session = perf_session__new(&file, false, NULL);
 	TEST_ASSERT_VAL("can't get session", session);
 
-	for (i = 0; i < session->header.env.nr_cpus_online; i++) {
+	for (i = 0; i < session->header.env.nr_cpus_avail; i++) {
+		if (!cpu_map__has(map, i))
+			continue;
 		pr_debug("CPU %d, core %d, socket %d\n", i,
 			 session->header.env.cpu[i].core_id,
 			 session->header.env.cpu[i].socket_id);
diff --git a/tools/perf/util/env.c b/tools/perf/util/env.c
index bb964e86b09d..075fc77286bf 100644
--- a/tools/perf/util/env.c
+++ b/tools/perf/util/env.c
@@ -66,7 +66,7 @@ int perf_env__read_cpu_topology_map(struct perf_env *env)
 		return 0;
 
 	if (env->nr_cpus_avail == 0)
-		env->nr_cpus_avail = sysconf(_SC_NPROCESSORS_CONF);
+		env->nr_cpus_avail = cpu__max_present_cpu();
 
 	nr_cpus = env->nr_cpus_avail;
 	if (nr_cpus == -1)
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index 2b7ed52df807..5c81e0853bff 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -293,11 +293,7 @@ static int write_nrcpus(int fd, struct perf_header *h __maybe_unused,
 	u32 nrc, nra;
 	int ret;
 
-	nr = sysconf(_SC_NPROCESSORS_CONF);
-	if (nr < 0)
-		return -1;
-
-	nrc = (u32)(nr & UINT_MAX);
+	nrc = cpu__max_present_cpu();
 
 	nr = sysconf(_SC_NPROCESSORS_ONLN);
 	if (nr < 0)
@@ -511,9 +507,7 @@ static struct cpu_topo *build_cpu_topology(void)
 	int ret = 0;
 	struct cpu_map *map;
 
-	ncpus = sysconf(_SC_NPROCESSORS_CONF);
-	if (ncpus < 0)
-		goto out;
+	ncpus = cpu__max_present_cpu();
 
 	/* build online CPU map */
 	map = cpu_map__new(NULL);
@@ -1138,7 +1132,7 @@ static void print_cpu_topology(struct perf_header *ph, int fd __maybe_unused,
 {
 	int nr, i;
 	char *str;
-	int cpu_nr = ph->env.nr_cpus_online;
+	int cpu_nr = ph->env.nr_cpus_avail;
 
 	nr = ph->env.nr_sibling_cores;
 	str = ph->env.sibling_cores;
@@ -1793,7 +1787,7 @@ static int process_cpu_topology(struct perf_file_section *section,
 	u32 nr, i;
 	char *str;
 	struct strbuf sb;
-	int cpu_nr = ph->env.nr_cpus_online;
+	int cpu_nr = ph->env.nr_cpus_avail;
 	u64 size = 0;
 
 	ph->env.cpu = calloc(cpu_nr, sizeof(*ph->env.cpu));
@@ -1874,7 +1868,7 @@ static int process_cpu_topology(struct perf_file_section *section,
 		if (ph->needs_swap)
 			nr = bswap_32(nr);
 
-		if (nr > (u32)cpu_nr) {
+		if (nr != (u32)-1 && nr > (u32)cpu_nr) {
 			pr_debug("socket_id number is too big."
 				 "You may need to upgrade the perf tool.\n");
 			goto free_cpu;
-- 
1.8.3.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* Re: [PATCH] perf: fix topology test on systems with sparse CPUs
  2017-01-31 16:03   ` Jan Stancek
@ 2017-02-02 11:29     ` Jiri Olsa
  2017-02-02 12:06       ` Jan Stancek
  2017-02-02 11:29     ` [PATCH] perf: fix topology test on systems with sparse CPUs Jiri Olsa
  1 sibling, 1 reply; 24+ messages in thread
From: Jiri Olsa @ 2017-02-02 11:29 UTC (permalink / raw)
  To: Jan Stancek
  Cc: linux-kernel, peterz, mingo, acme, alexander.shishkin, jolsa,
	mhiramat, rui.teng, sukadev

On Tue, Jan 31, 2017 at 05:03:51PM +0100, Jan Stancek wrote:
> On 01/30/2017 07:49 PM, Jiri Olsa wrote:
> > so basically we're changing from avail to online cpus
> > 
> > have you checked all the users of this FEATURE
> > if such change is ok?
> 
> Jiri,
> 
> It wasn't OK as there are other users who index cpu_topology_map by CPU id.
> I decided to give the alternative a try (attached): keep cpu_topology_map
> indexed by CPU id, but extend it to fit max present CPU.

please send this next time as a standard patchset,
it's hard to discuss over attachments

SNIP

> When build_cpu_topo() encounters offline/absent CPUs,
> it fails to find any sysfs entries and returns failure.
> This leads to build_cpu_topology() and write_cpu_topology()
> failing as well.
> 
> Because HEADER_CPU_TOPOLOGY has not been written, read leaves
> cpu_topology_map NULL and we get NULL ptr deref at:
> 
>  ...
>   cmd_test
>    __cmd_test
>     test_and_print
>      run_test
>       test_session_topology
>        check_cpu_topology

So IIUIC that's the key issue here.. write_cpu_topology that fails
to write the TOPO data and following readers crashing on processing
uncomplete data? if thats the case write_cpu_topology needs to
be fixed, instead of doing workarounds

SNIP

>  	u32 nr, i;
>  	size_t sz;
>  	long ncpus;
> -	int ret = -1;
> +	int ret = 0;
> +	struct cpu_map *map;
>  
>  	ncpus = sysconf(_SC_NPROCESSORS_CONF);
>  	if (ncpus < 0)
> -		return NULL;
> +		goto out;

can just return NULL

> +
> +	/* build online CPU map */
> +	map = cpu_map__new(NULL);
> +	if (map == NULL) {
> +		pr_debug("failed to get system cpumap\n");
> +		goto out;
> +	}
>  
>  	nr = (u32)(ncpus & UINT_MAX);
>  
>  	sz = nr * sizeof(char *);
> -
>  	addr = calloc(1, sizeof(*tp) + 2 * sz);
>  	if (!addr)
> -		return NULL;
> +		goto out_free;
>  
>  	tp = addr;
>  	tp->cpu_nr = nr;
> @@ -530,14 +537,21 @@ static struct cpu_topo *build_cpu_topology(void)
>  	tp->thread_siblings = addr;
>  
>  	for (i = 0; i < nr; i++) {
> +		if (!cpu_map__has(map, i))
> +			continue;
> +

so this prevents build_cpu_topo to fail due to missing topology
info because cpu is offline.. can it fail for other reasons?


>  		ret = build_cpu_topo(tp, i);
>  		if (ret < 0)
>  			break;


SNIP

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH] perf: fix topology test on systems with sparse CPUs
  2017-01-31 16:03   ` Jan Stancek
  2017-02-02 11:29     ` Jiri Olsa
@ 2017-02-02 11:29     ` Jiri Olsa
  1 sibling, 0 replies; 24+ messages in thread
From: Jiri Olsa @ 2017-02-02 11:29 UTC (permalink / raw)
  To: Jan Stancek
  Cc: linux-kernel, peterz, mingo, acme, alexander.shishkin, jolsa,
	mhiramat, rui.teng, sukadev

On Tue, Jan 31, 2017 at 05:03:51PM +0100, Jan Stancek wrote:

SNIP

> 
> There are 2 problems wrt. cpu_topology_map on systems with sparse CPUs:
> 
> 1. offline/absent CPUs will have their socket_id and core_id set to -1
>    which triggers:
>    "socket_id number is too big.You may need to upgrade the perf tool."
> 
> 2. size of cpu_topology_map (perf_env.cpu[]) is allocated based on
>    _SC_NPROCESSORS_CONF, but can be indexed with CPU ids going above.
>    Users of perf_env.cpu[] are using CPU id as index. This can lead
>    to read beyond what was allocated:
>    ==19991== Invalid read of size 4
>    ==19991==    at 0x490CEB: check_cpu_topology (topology.c:69)
>    ==19991==    by 0x490CEB: test_session_topology (topology.c:106)
>    ...
> 
> For example:
>   _SC_NPROCESSORS_CONF == 16
>   available: 2 nodes (0-1)
>   node 0 cpus: 0 6 8 10 16 22 24 26
>   node 0 size: 12004 MB
>   node 0 free: 9470 MB
>   node 1 cpus: 1 7 9 11 23 25 27
>   node 1 size: 12093 MB
>   node 1 free: 9406 MB
>   node distances:
>   node   0   1
>     0:  10  20
>     1:  20  10

so what's max_present_cpu in this example?

jirka

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH] perf: fix topology test on systems with sparse CPUs
  2017-02-02 11:29     ` Jiri Olsa
@ 2017-02-02 12:06       ` Jan Stancek
  2017-02-02 13:01         ` Jiri Olsa
  0 siblings, 1 reply; 24+ messages in thread
From: Jan Stancek @ 2017-02-02 12:06 UTC (permalink / raw)
  To: Jiri Olsa
  Cc: linux-kernel, peterz, mingo, acme, alexander shishkin, jolsa,
	mhiramat, rui teng, sukadev, jstancek

> 
> > When build_cpu_topo() encounters offline/absent CPUs,
> > it fails to find any sysfs entries and returns failure.
> > This leads to build_cpu_topology() and write_cpu_topology()
> > failing as well.
> > 
> > Because HEADER_CPU_TOPOLOGY has not been written, read leaves
> > cpu_topology_map NULL and we get NULL ptr deref at:
> > 
> >  ...
> >   cmd_test
> >    __cmd_test
> >     test_and_print
> >      run_test
> >       test_session_topology
> >        check_cpu_topology
> 
> So IIUIC that's the key issue here.. write_cpu_topology that fails
> to write the TOPO data and following readers crashing on processing
> uncomplete data? if thats the case write_cpu_topology needs to
> be fixed, instead of doing workarounds

It's already late when you are in write_cpu_topology(), because
build_cpu_topology() returned you NULL - there's nothing to write.
That's why patch aims to fix this in build_cpu_topology().

> 
> SNIP
> 
> >  	u32 nr, i;
> >  	size_t sz;
> >  	long ncpus;
> > -	int ret = -1;
> > +	int ret = 0;
> > +	struct cpu_map *map;
> >  
> >  	ncpus = sysconf(_SC_NPROCESSORS_CONF);
> >  	if (ncpus < 0)
> > -		return NULL;
> > +		goto out;
> 
> can just return NULL
> 
> > +
> > +	/* build online CPU map */
> > +	map = cpu_map__new(NULL);
> > +	if (map == NULL) {
> > +		pr_debug("failed to get system cpumap\n");
> > +		goto out;
> > +	}
> >  
> >  	nr = (u32)(ncpus & UINT_MAX);
> >  
> >  	sz = nr * sizeof(char *);
> > -
> >  	addr = calloc(1, sizeof(*tp) + 2 * sz);
> >  	if (!addr)
> > -		return NULL;
> > +		goto out_free;
> >  
> >  	tp = addr;
> >  	tp->cpu_nr = nr;
> > @@ -530,14 +537,21 @@ static struct cpu_topo *build_cpu_topology(void)
> >  	tp->thread_siblings = addr;
> >  
> >  	for (i = 0; i < nr; i++) {
> > +		if (!cpu_map__has(map, i))
> > +			continue;
> > +
> 
> so this prevents build_cpu_topo to fail due to missing topology
> info because cpu is offline.. can it fail for other reasons?

It's unlikely, though I suppose if you couldn't open and read something
from sysfs (say sysfs is not mounted) it can fail for online CPU too.

> 
> 
> >  		ret = build_cpu_topo(tp, i);
> >  		if (ret < 0)
> >  			break;
> 

SNIP

> For example:
>   _SC_NPROCESSORS_CONF == 16
>   available: 2 nodes (0-1)
>   node 0 cpus: 0 6 8 10 16 22 24 26
>   node 0 size: 12004 MB
>   node 0 free: 9470 MB
>   node 1 cpus: 1 7 9 11 23 25 27
>   node 1 size: 12093 MB
>   node 1 free: 9406 MB
>   node distances:
>   node   0   1
>     0:  10  20
>     1:  20  10
> so what's max_present_cpu in this example?

It's 28, which is the number of core_id/socket_id entries,
for CPUs 0 up to 27.

Regards,
Jan

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH] perf: fix topology test on systems with sparse CPUs
  2017-02-02 12:06       ` Jan Stancek
@ 2017-02-02 13:01         ` Jiri Olsa
  2017-02-13 15:34           ` [PATCH v2 1/3] perf: add cpu__max_present_cpu() Jan Stancek
  0 siblings, 1 reply; 24+ messages in thread
From: Jiri Olsa @ 2017-02-02 13:01 UTC (permalink / raw)
  To: Jan Stancek
  Cc: linux-kernel, peterz, mingo, acme, alexander shishkin, jolsa,
	mhiramat, rui teng, sukadev

On Thu, Feb 02, 2017 at 07:06:43AM -0500, Jan Stancek wrote:
> > 
> > > When build_cpu_topo() encounters offline/absent CPUs,
> > > it fails to find any sysfs entries and returns failure.
> > > This leads to build_cpu_topology() and write_cpu_topology()
> > > failing as well.
> > > 
> > > Because HEADER_CPU_TOPOLOGY has not been written, read leaves
> > > cpu_topology_map NULL and we get NULL ptr deref at:
> > > 
> > >  ...
> > >   cmd_test
> > >    __cmd_test
> > >     test_and_print
> > >      run_test
> > >       test_session_topology
> > >        check_cpu_topology
> > 
> > So IIUIC that's the key issue here.. write_cpu_topology that fails
> > to write the TOPO data and following readers crashing on processing
> > uncomplete data? if thats the case write_cpu_topology needs to
> > be fixed, instead of doing workarounds
> 
> It's already late when you are in write_cpu_topology(), because
> build_cpu_topology() returned you NULL - there's nothing to write.
> That's why patch aims to fix this in build_cpu_topology().

ok, then we need to make sure we can't fail in write_cpu_topology

might be another patch scope though.. we can go with your fix so far

SNIP

> 
> > For example:
> >   _SC_NPROCESSORS_CONF == 16
> >   available: 2 nodes (0-1)
> >   node 0 cpus: 0 6 8 10 16 22 24 26
> >   node 0 size: 12004 MB
> >   node 0 free: 9470 MB
> >   node 1 cpus: 1 7 9 11 23 25 27
> >   node 1 size: 12093 MB
> >   node 1 free: 9406 MB
> >   node distances:
> >   node   0   1
> >     0:  10  20
> >     1:  20  10
> > so what's max_present_cpu in this example?
> 
> It's 28, which is the number of core_id/socket_id entries,
> for CPUs 0 up to 27.

ok, good

jirka

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH v2 1/3] perf: add cpu__max_present_cpu()
  2017-02-02 13:01         ` Jiri Olsa
@ 2017-02-13 15:34           ` Jan Stancek
  2017-02-13 15:34             ` [PATCH v2 2/3] perf: make build_cpu_topology skip offline/absent CPUs Jan Stancek
                               ` (2 more replies)
  0 siblings, 3 replies; 24+ messages in thread
From: Jan Stancek @ 2017-02-13 15:34 UTC (permalink / raw)
  To: linux-kernel
  Cc: peterz, mingo, acme, alexander.shishkin, jolsa, mhiramat, jstancek

Similar to cpu__max_cpu() (which returns max possible CPU),
returns max present CPU.

Signed-off-by: Jan Stancek <jstancek@redhat.com>
---
 tools/perf/util/cpumap.c | 22 ++++++++++++++++++++++
 tools/perf/util/cpumap.h |  1 +
 2 files changed, 23 insertions(+)

diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c
index 2c0b52264a46..8c7504939113 100644
--- a/tools/perf/util/cpumap.c
+++ b/tools/perf/util/cpumap.c
@@ -9,6 +9,7 @@
 #include "asm/bug.h"
 
 static int max_cpu_num;
+static int max_present_cpu_num;
 static int max_node_num;
 static int *cpunode_map;
 
@@ -442,6 +443,7 @@ static void set_max_cpu_num(void)
 
 	/* set up default */
 	max_cpu_num = 4096;
+	max_present_cpu_num = 4096;
 
 	mnt = sysfs__mountpoint();
 	if (!mnt)
@@ -455,6 +457,17 @@ static void set_max_cpu_num(void)
 	}
 
 	ret = get_max_num(path, &max_cpu_num);
+	if (ret)
+		goto out;
+
+	/* get the highest present cpu number for a sparse allocation */
+	ret = snprintf(path, PATH_MAX, "%s/devices/system/cpu/present", mnt);
+	if (ret == PATH_MAX) {
+		pr_err("sysfs path crossed PATH_MAX(%d) size\n", PATH_MAX);
+		goto out;
+	}
+
+	ret = get_max_num(path, &max_present_cpu_num);
 
 out:
 	if (ret)
@@ -505,6 +518,15 @@ int cpu__max_cpu(void)
 	return max_cpu_num;
 }
 
+int cpu__max_present_cpu(void)
+{
+	if (unlikely(!max_present_cpu_num))
+		set_max_cpu_num();
+
+	return max_present_cpu_num;
+}
+
+
 int cpu__get_node(int cpu)
 {
 	if (unlikely(cpunode_map == NULL)) {
diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h
index 06bd689f5989..1a0549af8f5c 100644
--- a/tools/perf/util/cpumap.h
+++ b/tools/perf/util/cpumap.h
@@ -62,6 +62,7 @@ static inline bool cpu_map__empty(const struct cpu_map *map)
 
 int cpu__max_node(void);
 int cpu__max_cpu(void);
+int cpu__max_present_cpu(void);
 int cpu__get_node(int cpu);
 
 int cpu_map__build_map(struct cpu_map *cpus, struct cpu_map **res,
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v2 2/3] perf: make build_cpu_topology skip offline/absent CPUs
  2017-02-13 15:34           ` [PATCH v2 1/3] perf: add cpu__max_present_cpu() Jan Stancek
@ 2017-02-13 15:34             ` Jan Stancek
  2017-02-14 11:01               ` Jiri Olsa
  2017-02-13 15:34             ` [PATCH v2 3/3] perf: replace _SC_NPROCESSORS_CONF with max_present_cpu in cpu_topology_map Jan Stancek
  2017-02-14 11:17             ` [PATCH v2 1/3] perf: add cpu__max_present_cpu() Jiri Olsa
  2 siblings, 1 reply; 24+ messages in thread
From: Jan Stancek @ 2017-02-13 15:34 UTC (permalink / raw)
  To: linux-kernel
  Cc: peterz, mingo, acme, alexander.shishkin, jolsa, mhiramat, jstancek

When build_cpu_topo() encounters offline/absent CPUs,
it fails to find any sysfs entries and returns failure.
This leads to build_cpu_topology() and write_cpu_topology()
failing as well.

Because HEADER_CPU_TOPOLOGY has not been written, read leaves
cpu_topology_map NULL and we get NULL ptr deref at:

 ...
  cmd_test
   __cmd_test
    test_and_print
     run_test
      test_session_topology
       check_cpu_topology

36: Session topology                           :
--- start ---
test child forked, pid 14902
templ file: /tmp/perf-test-4CKocW
failed to write feature HEADER_CPU_TOPOLOGY
perf: Segmentation fault
Obtained 9 stack frames.
./perf(sighandler_dump_stack+0x41) [0x5095f1]
/lib64/libc.so.6(+0x35250) [0x7f4b7c3c9250]
./perf(test_session_topology+0x1db) [0x490ceb]
./perf() [0x475b68]
./perf(cmd_test+0x5b9) [0x4763c9]
./perf() [0x4945a3]
./perf(main+0x69f) [0x427e8f]
/lib64/libc.so.6(__libc_start_main+0xf5) [0x7f4b7c3b5b35]
./perf() [0x427fb9]
test child interrupted
---- end ----
Session topology: FAILED!

This patch makes build_cpu_topology() skip offline/absent CPUs,
by checking their presence against cpu_map built from online CPUs.

Signed-off-by: Jan Stancek <jstancek@redhat.com>
---
 tools/perf/util/header.c | 21 +++++++++++++++++----
 1 file changed, 17 insertions(+), 4 deletions(-)

Changes in v2:
- drop out label, use return NULL where possible

diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index d89c9c7ef4e5..4b0ea4e92e9d 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -503,24 +503,31 @@ static void free_cpu_topo(struct cpu_topo *tp)
 
 static struct cpu_topo *build_cpu_topology(void)
 {
-	struct cpu_topo *tp;
+	struct cpu_topo *tp = NULL;
 	void *addr;
 	u32 nr, i;
 	size_t sz;
 	long ncpus;
-	int ret = -1;
+	int ret = 0;
+	struct cpu_map *map;
 
 	ncpus = sysconf(_SC_NPROCESSORS_CONF);
 	if (ncpus < 0)
 		return NULL;
 
+	/* build online CPU map */
+	map = cpu_map__new(NULL);
+	if (map == NULL) {
+		pr_debug("failed to get system cpumap\n");
+		return NULL;
+	}
+
 	nr = (u32)(ncpus & UINT_MAX);
 
 	sz = nr * sizeof(char *);
-
 	addr = calloc(1, sizeof(*tp) + 2 * sz);
 	if (!addr)
-		return NULL;
+		goto out_free;
 
 	tp = addr;
 	tp->cpu_nr = nr;
@@ -530,10 +537,16 @@ static struct cpu_topo *build_cpu_topology(void)
 	tp->thread_siblings = addr;
 
 	for (i = 0; i < nr; i++) {
+		if (!cpu_map__has(map, i))
+			continue;
+
 		ret = build_cpu_topo(tp, i);
 		if (ret < 0)
 			break;
 	}
+
+out_free:
+	cpu_map__put(map);
 	if (ret) {
 		free_cpu_topo(tp);
 		tp = NULL;
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v2 3/3] perf: replace _SC_NPROCESSORS_CONF with max_present_cpu in cpu_topology_map
  2017-02-13 15:34           ` [PATCH v2 1/3] perf: add cpu__max_present_cpu() Jan Stancek
  2017-02-13 15:34             ` [PATCH v2 2/3] perf: make build_cpu_topology skip offline/absent CPUs Jan Stancek
@ 2017-02-13 15:34             ` Jan Stancek
  2017-02-14 11:17             ` [PATCH v2 1/3] perf: add cpu__max_present_cpu() Jiri Olsa
  2 siblings, 0 replies; 24+ messages in thread
From: Jan Stancek @ 2017-02-13 15:34 UTC (permalink / raw)
  To: linux-kernel
  Cc: peterz, mingo, acme, alexander.shishkin, jolsa, mhiramat, jstancek

There are 2 problems wrt. cpu_topology_map on systems with sparse CPUs:

1. offline/absent CPUs will have their socket_id and core_id set to -1
   which triggers:
   "socket_id number is too big.You may need to upgrade the perf tool."

2. size of cpu_topology_map (perf_env.cpu[]) is allocated based on
   _SC_NPROCESSORS_CONF, but can be indexed with CPU ids going above.
   Users of perf_env.cpu[] are using CPU id as index. This can lead
   to read beyond what was allocated:
   ==19991== Invalid read of size 4
   ==19991==    at 0x490CEB: check_cpu_topology (topology.c:69)
   ==19991==    by 0x490CEB: test_session_topology (topology.c:106)
   ...

For example:
  _SC_NPROCESSORS_CONF == 16
  available: 2 nodes (0-1)
  node 0 cpus: 0 6 8 10 16 22 24 26
  node 0 size: 12004 MB
  node 0 free: 9470 MB
  node 1 cpus: 1 7 9 11 23 25 27
  node 1 size: 12093 MB
  node 1 free: 9406 MB
  node distances:
  node   0   1
    0:  10  20
    1:  20  10

This patch changes HEADER_NRCPUS.nr_cpus_available from _SC_NPROCESSORS_CONF
to max_present_cpu and updates any user of cpu_topology_map to iterate
with nr_cpus_avail. As consequence HEADER_CPU_TOPOLOGY core_id and
socket_id lists get longer, but maintain compatibility with pre-patch
state - index to cpu_topology_map is CPU id.

 ./perf test 36 -v
36: Session topology                           :
--- start ---
test child forked, pid 22211
templ file: /tmp/perf-test-gmdX5i
CPU 0, core 0, socket 0
CPU 1, core 0, socket 1
CPU 6, core 10, socket 0
CPU 7, core 10, socket 1
CPU 8, core 1, socket 0
CPU 9, core 1, socket 1
CPU 10, core 9, socket 0
CPU 11, core 9, socket 1
CPU 16, core 0, socket 0
CPU 22, core 10, socket 0
CPU 23, core 10, socket 1
CPU 24, core 1, socket 0
CPU 25, core 1, socket 1
CPU 26, core 9, socket 0
CPU 27, core 9, socket 1
test child finished with 0
---- end ----
Session topology: Ok

Signed-off-by: Jan Stancek <jstancek@redhat.com>
---
 tools/perf/builtin-stat.c   |  2 +-
 tools/perf/tests/topology.c |  4 +++-
 tools/perf/util/env.c       |  2 +-
 tools/perf/util/header.c    | 16 +++++-----------
 4 files changed, 10 insertions(+), 14 deletions(-)

diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c
index a02f2e965628..9e2be1c52fbd 100644
--- a/tools/perf/builtin-stat.c
+++ b/tools/perf/builtin-stat.c
@@ -1765,7 +1765,7 @@ static inline int perf_env__get_cpu(struct perf_env *env, struct cpu_map *map, i
 
 	cpu = map->map[idx];
 
-	if (cpu >= env->nr_cpus_online)
+	if (cpu >= env->nr_cpus_avail)
 		return -1;
 
 	return cpu;
diff --git a/tools/perf/tests/topology.c b/tools/perf/tests/topology.c
index 98fe69ac553c..803f893550d6 100644
--- a/tools/perf/tests/topology.c
+++ b/tools/perf/tests/topology.c
@@ -65,7 +65,9 @@ static int check_cpu_topology(char *path, struct cpu_map *map)
 	session = perf_session__new(&file, false, NULL);
 	TEST_ASSERT_VAL("can't get session", session);
 
-	for (i = 0; i < session->header.env.nr_cpus_online; i++) {
+	for (i = 0; i < session->header.env.nr_cpus_avail; i++) {
+		if (!cpu_map__has(map, i))
+			continue;
 		pr_debug("CPU %d, core %d, socket %d\n", i,
 			 session->header.env.cpu[i].core_id,
 			 session->header.env.cpu[i].socket_id);
diff --git a/tools/perf/util/env.c b/tools/perf/util/env.c
index bb964e86b09d..075fc77286bf 100644
--- a/tools/perf/util/env.c
+++ b/tools/perf/util/env.c
@@ -66,7 +66,7 @@ int perf_env__read_cpu_topology_map(struct perf_env *env)
 		return 0;
 
 	if (env->nr_cpus_avail == 0)
-		env->nr_cpus_avail = sysconf(_SC_NPROCESSORS_CONF);
+		env->nr_cpus_avail = cpu__max_present_cpu();
 
 	nr_cpus = env->nr_cpus_avail;
 	if (nr_cpus == -1)
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index 4b0ea4e92e9d..e9384d17ae7d 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -293,11 +293,7 @@ static int write_nrcpus(int fd, struct perf_header *h __maybe_unused,
 	u32 nrc, nra;
 	int ret;
 
-	nr = sysconf(_SC_NPROCESSORS_CONF);
-	if (nr < 0)
-		return -1;
-
-	nrc = (u32)(nr & UINT_MAX);
+	nrc = cpu__max_present_cpu();
 
 	nr = sysconf(_SC_NPROCESSORS_ONLN);
 	if (nr < 0)
@@ -511,9 +507,7 @@ static struct cpu_topo *build_cpu_topology(void)
 	int ret = 0;
 	struct cpu_map *map;
 
-	ncpus = sysconf(_SC_NPROCESSORS_CONF);
-	if (ncpus < 0)
-		return NULL;
+	ncpus = cpu__max_present_cpu();
 
 	/* build online CPU map */
 	map = cpu_map__new(NULL);
@@ -1137,7 +1131,7 @@ static void print_cpu_topology(struct perf_header *ph, int fd __maybe_unused,
 {
 	int nr, i;
 	char *str;
-	int cpu_nr = ph->env.nr_cpus_online;
+	int cpu_nr = ph->env.nr_cpus_avail;
 
 	nr = ph->env.nr_sibling_cores;
 	str = ph->env.sibling_cores;
@@ -1792,7 +1786,7 @@ static int process_cpu_topology(struct perf_file_section *section,
 	u32 nr, i;
 	char *str;
 	struct strbuf sb;
-	int cpu_nr = ph->env.nr_cpus_online;
+	int cpu_nr = ph->env.nr_cpus_avail;
 	u64 size = 0;
 
 	ph->env.cpu = calloc(cpu_nr, sizeof(*ph->env.cpu));
@@ -1873,7 +1867,7 @@ static int process_cpu_topology(struct perf_file_section *section,
 		if (ph->needs_swap)
 			nr = bswap_32(nr);
 
-		if (nr > (u32)cpu_nr) {
+		if (nr != (u32)-1 && nr > (u32)cpu_nr) {
 			pr_debug("socket_id number is too big."
 				 "You may need to upgrade the perf tool.\n");
 			goto free_cpu;
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* Re: [PATCH v2 2/3] perf: make build_cpu_topology skip offline/absent CPUs
  2017-02-13 15:34             ` [PATCH v2 2/3] perf: make build_cpu_topology skip offline/absent CPUs Jan Stancek
@ 2017-02-14 11:01               ` Jiri Olsa
  2017-02-15  8:48                 ` Jan Stancek
  0 siblings, 1 reply; 24+ messages in thread
From: Jiri Olsa @ 2017-02-14 11:01 UTC (permalink / raw)
  To: Jan Stancek
  Cc: linux-kernel, peterz, mingo, acme, alexander.shishkin, jolsa, mhiramat

On Mon, Feb 13, 2017 at 04:34:35PM +0100, Jan Stancek wrote:

SNIP

> This patch makes build_cpu_topology() skip offline/absent CPUs,
> by checking their presence against cpu_map built from online CPUs.
> 
> Signed-off-by: Jan Stancek <jstancek@redhat.com>
> ---
>  tools/perf/util/header.c | 21 +++++++++++++++++----
>  1 file changed, 17 insertions(+), 4 deletions(-)
> 
> Changes in v2:
> - drop out label, use return NULL where possible
> 
> diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
> index d89c9c7ef4e5..4b0ea4e92e9d 100644
> --- a/tools/perf/util/header.c
> +++ b/tools/perf/util/header.c
> @@ -503,24 +503,31 @@ static void free_cpu_topo(struct cpu_topo *tp)
>  
>  static struct cpu_topo *build_cpu_topology(void)
>  {
> -	struct cpu_topo *tp;
> +	struct cpu_topo *tp = NULL;
>  	void *addr;
>  	u32 nr, i;
>  	size_t sz;
>  	long ncpus;
> -	int ret = -1;
> +	int ret = 0;

hum, shoudn't we fail if we dont find any online cpu?

jirka

> +	struct cpu_map *map;
>  
>  	ncpus = sysconf(_SC_NPROCESSORS_CONF);
>  	if (ncpus < 0)
>  		return NULL;
>  
> +	/* build online CPU map */
> +	map = cpu_map__new(NULL);
> +	if (map == NULL) {
> +		pr_debug("failed to get system cpumap\n");
> +		return NULL;
> +	}
> +
>  	nr = (u32)(ncpus & UINT_MAX);
>  
>  	sz = nr * sizeof(char *);
> -
>  	addr = calloc(1, sizeof(*tp) + 2 * sz);
>  	if (!addr)
> -		return NULL;
> +		goto out_free;
>  
>  	tp = addr;
>  	tp->cpu_nr = nr;
> @@ -530,10 +537,16 @@ static struct cpu_topo *build_cpu_topology(void)
>  	tp->thread_siblings = addr;
>  
>  	for (i = 0; i < nr; i++) {
> +		if (!cpu_map__has(map, i))
> +			continue;
> +
>  		ret = build_cpu_topo(tp, i);
>  		if (ret < 0)
>  			break;
>  	}
> +
> +out_free:
> +	cpu_map__put(map);
>  	if (ret) {
>  		free_cpu_topo(tp);
>  		tp = NULL;
> -- 
> 1.8.3.1
> 

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v2 1/3] perf: add cpu__max_present_cpu()
  2017-02-13 15:34           ` [PATCH v2 1/3] perf: add cpu__max_present_cpu() Jan Stancek
  2017-02-13 15:34             ` [PATCH v2 2/3] perf: make build_cpu_topology skip offline/absent CPUs Jan Stancek
  2017-02-13 15:34             ` [PATCH v2 3/3] perf: replace _SC_NPROCESSORS_CONF with max_present_cpu in cpu_topology_map Jan Stancek
@ 2017-02-14 11:17             ` Jiri Olsa
  2 siblings, 0 replies; 24+ messages in thread
From: Jiri Olsa @ 2017-02-14 11:17 UTC (permalink / raw)
  To: Jan Stancek
  Cc: linux-kernel, peterz, mingo, acme, alexander.shishkin, jolsa, mhiramat

On Mon, Feb 13, 2017 at 04:34:34PM +0100, Jan Stancek wrote:
> Similar to cpu__max_cpu() (which returns max possible CPU),
> returns max present CPU.
> 
> Signed-off-by: Jan Stancek <jstancek@redhat.com>
> ---
>  tools/perf/util/cpumap.c | 22 ++++++++++++++++++++++
>  tools/perf/util/cpumap.h |  1 +
>  2 files changed, 23 insertions(+)
> 
> diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c
> index 2c0b52264a46..8c7504939113 100644
> --- a/tools/perf/util/cpumap.c
> +++ b/tools/perf/util/cpumap.c
> @@ -9,6 +9,7 @@
>  #include "asm/bug.h"
>  
>  static int max_cpu_num;
> +static int max_present_cpu_num;

I think it'd be less confusing for me if we follow the kernel names here

 static int max_cpu_possible;
 static int max_cpu_present;

also I wonder we should use 

 static int max_cpu_online;

instead of:

 sysconf(_SC_NPROCESSORS_ONLN)

but we can do it all later on as follow up

jirka

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v2 2/3] perf: make build_cpu_topology skip offline/absent CPUs
  2017-02-14 11:01               ` Jiri Olsa
@ 2017-02-15  8:48                 ` Jan Stancek
  2017-02-17 11:10                   ` [PATCH v3 1/3] perf: add cpu__max_present_cpu() Jan Stancek
  0 siblings, 1 reply; 24+ messages in thread
From: Jan Stancek @ 2017-02-15  8:48 UTC (permalink / raw)
  To: Jiri Olsa
  Cc: linux-kernel, peterz, mingo, acme, alexander shishkin, jolsa, mhiramat



----- Original Message -----
> From: "Jiri Olsa" <jolsa@redhat.com>
> To: "Jan Stancek" <jstancek@redhat.com>
> Cc: linux-kernel@vger.kernel.org, peterz@infradead.org, mingo@redhat.com, acme@kernel.org, "alexander shishkin"
> <alexander.shishkin@linux.intel.com>, jolsa@kernel.org, mhiramat@kernel.org
> Sent: Tuesday, 14 February, 2017 12:01:10 PM
> Subject: Re: [PATCH v2 2/3] perf: make build_cpu_topology skip offline/absent CPUs
> 
> On Mon, Feb 13, 2017 at 04:34:35PM +0100, Jan Stancek wrote:
> 
> SNIP
> 
> > This patch makes build_cpu_topology() skip offline/absent CPUs,
> > by checking their presence against cpu_map built from online CPUs.
> > 
> > Signed-off-by: Jan Stancek <jstancek@redhat.com>
> > ---
> >  tools/perf/util/header.c | 21 +++++++++++++++++----
> >  1 file changed, 17 insertions(+), 4 deletions(-)
> > 
> > Changes in v2:
> > - drop out label, use return NULL where possible
> > 
> > diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
> > index d89c9c7ef4e5..4b0ea4e92e9d 100644
> > --- a/tools/perf/util/header.c
> > +++ b/tools/perf/util/header.c
> > @@ -503,24 +503,31 @@ static void free_cpu_topo(struct cpu_topo *tp)
> >  
> >  static struct cpu_topo *build_cpu_topology(void)
> >  {
> > -	struct cpu_topo *tp;
> > +	struct cpu_topo *tp = NULL;
> >  	void *addr;
> >  	u32 nr, i;
> >  	size_t sz;
> >  	long ncpus;
> > -	int ret = -1;
> > +	int ret = 0;
> 
> hum, shoudn't we fail if we dont find any online cpu?
> 
> jirka

You're right, there's no reason to change this. I'll send v3.

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH v3 1/3] perf: add cpu__max_present_cpu()
  2017-02-15  8:48                 ` Jan Stancek
@ 2017-02-17 11:10                   ` Jan Stancek
  2017-02-17 11:10                     ` [PATCH v3 2/3] perf: make build_cpu_topology skip offline/absent CPUs Jan Stancek
                                       ` (2 more replies)
  0 siblings, 3 replies; 24+ messages in thread
From: Jan Stancek @ 2017-02-17 11:10 UTC (permalink / raw)
  To: linux-kernel; +Cc: peterz, mingo, acme, alexander.shishkin, jolsa, mhiramat

Similar to cpu__max_cpu() (which returns max possible CPU),
returns max present CPU.

Signed-off-by: Jan Stancek <jstancek@redhat.com>
---
 tools/perf/util/cpumap.c | 22 ++++++++++++++++++++++
 tools/perf/util/cpumap.h |  1 +
 2 files changed, 23 insertions(+)

diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c
index 2c0b52264a46..8c7504939113 100644
--- a/tools/perf/util/cpumap.c
+++ b/tools/perf/util/cpumap.c
@@ -9,6 +9,7 @@
 #include "asm/bug.h"
 
 static int max_cpu_num;
+static int max_present_cpu_num;
 static int max_node_num;
 static int *cpunode_map;
 
@@ -442,6 +443,7 @@ static void set_max_cpu_num(void)
 
 	/* set up default */
 	max_cpu_num = 4096;
+	max_present_cpu_num = 4096;
 
 	mnt = sysfs__mountpoint();
 	if (!mnt)
@@ -455,6 +457,17 @@ static void set_max_cpu_num(void)
 	}
 
 	ret = get_max_num(path, &max_cpu_num);
+	if (ret)
+		goto out;
+
+	/* get the highest present cpu number for a sparse allocation */
+	ret = snprintf(path, PATH_MAX, "%s/devices/system/cpu/present", mnt);
+	if (ret == PATH_MAX) {
+		pr_err("sysfs path crossed PATH_MAX(%d) size\n", PATH_MAX);
+		goto out;
+	}
+
+	ret = get_max_num(path, &max_present_cpu_num);
 
 out:
 	if (ret)
@@ -505,6 +518,15 @@ int cpu__max_cpu(void)
 	return max_cpu_num;
 }
 
+int cpu__max_present_cpu(void)
+{
+	if (unlikely(!max_present_cpu_num))
+		set_max_cpu_num();
+
+	return max_present_cpu_num;
+}
+
+
 int cpu__get_node(int cpu)
 {
 	if (unlikely(cpunode_map == NULL)) {
diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h
index 06bd689f5989..1a0549af8f5c 100644
--- a/tools/perf/util/cpumap.h
+++ b/tools/perf/util/cpumap.h
@@ -62,6 +62,7 @@ static inline bool cpu_map__empty(const struct cpu_map *map)
 
 int cpu__max_node(void);
 int cpu__max_cpu(void);
+int cpu__max_present_cpu(void);
 int cpu__get_node(int cpu);
 
 int cpu_map__build_map(struct cpu_map *cpus, struct cpu_map **res,
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v3 2/3] perf: make build_cpu_topology skip offline/absent CPUs
  2017-02-17 11:10                   ` [PATCH v3 1/3] perf: add cpu__max_present_cpu() Jan Stancek
@ 2017-02-17 11:10                     ` Jan Stancek
  2017-02-17 15:36                       ` Arnaldo Carvalho de Melo
  2017-02-21  8:12                       ` [tip:perf/urgent] perf header: Make " tip-bot for Jan Stancek
  2017-02-17 11:10                     ` [PATCH v3 3/3] perf: replace _SC_NPROCESSORS_CONF with max_present_cpu in cpu_topology_map Jan Stancek
  2017-02-21  8:11                     ` [tip:perf/urgent] perf cpumap: Add cpu__max_present_cpu() tip-bot for Jan Stancek
  2 siblings, 2 replies; 24+ messages in thread
From: Jan Stancek @ 2017-02-17 11:10 UTC (permalink / raw)
  To: linux-kernel; +Cc: peterz, mingo, acme, alexander.shishkin, jolsa, mhiramat

When build_cpu_topo() encounters offline/absent CPUs,
it fails to find any sysfs entries and returns failure.
This leads to build_cpu_topology() and write_cpu_topology()
failing as well.

Because HEADER_CPU_TOPOLOGY has not been written, read leaves
cpu_topology_map NULL and we get NULL ptr deref at:

 ...
  cmd_test
   __cmd_test
    test_and_print
     run_test
      test_session_topology
       check_cpu_topology

36: Session topology                           :
--- start ---
test child forked, pid 14902
templ file: /tmp/perf-test-4CKocW
failed to write feature HEADER_CPU_TOPOLOGY
perf: Segmentation fault
Obtained 9 stack frames.
./perf(sighandler_dump_stack+0x41) [0x5095f1]
/lib64/libc.so.6(+0x35250) [0x7f4b7c3c9250]
./perf(test_session_topology+0x1db) [0x490ceb]
./perf() [0x475b68]
./perf(cmd_test+0x5b9) [0x4763c9]
./perf() [0x4945a3]
./perf(main+0x69f) [0x427e8f]
/lib64/libc.so.6(__libc_start_main+0xf5) [0x7f4b7c3b5b35]
./perf() [0x427fb9]
test child interrupted
---- end ----
Session topology: FAILED!

This patch makes build_cpu_topology() skip offline/absent CPUs,
by checking their presence against cpu_map built from online CPUs.

Signed-off-by: Jan Stancek <jstancek@redhat.com>
---
 tools/perf/util/header.c | 19 ++++++++++++++++---
 1 file changed, 16 insertions(+), 3 deletions(-)

Changes in v3:
- make ret = -1 by default, so we fail in case build_cpu_topo
  doesn't run at all

diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index d89c9c7ef4e5..ca0f12fb5fd3 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -503,24 +503,31 @@ static void free_cpu_topo(struct cpu_topo *tp)
 
 static struct cpu_topo *build_cpu_topology(void)
 {
-	struct cpu_topo *tp;
+	struct cpu_topo *tp = NULL;
 	void *addr;
 	u32 nr, i;
 	size_t sz;
 	long ncpus;
 	int ret = -1;
+	struct cpu_map *map;
 
 	ncpus = sysconf(_SC_NPROCESSORS_CONF);
 	if (ncpus < 0)
 		return NULL;
 
+	/* build online CPU map */
+	map = cpu_map__new(NULL);
+	if (map == NULL) {
+		pr_debug("failed to get system cpumap\n");
+		return NULL;
+	}
+
 	nr = (u32)(ncpus & UINT_MAX);
 
 	sz = nr * sizeof(char *);
-
 	addr = calloc(1, sizeof(*tp) + 2 * sz);
 	if (!addr)
-		return NULL;
+		goto out_free;
 
 	tp = addr;
 	tp->cpu_nr = nr;
@@ -530,10 +537,16 @@ static struct cpu_topo *build_cpu_topology(void)
 	tp->thread_siblings = addr;
 
 	for (i = 0; i < nr; i++) {
+		if (!cpu_map__has(map, i))
+			continue;
+
 		ret = build_cpu_topo(tp, i);
 		if (ret < 0)
 			break;
 	}
+
+out_free:
+	cpu_map__put(map);
 	if (ret) {
 		free_cpu_topo(tp);
 		tp = NULL;
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v3 3/3] perf: replace _SC_NPROCESSORS_CONF with max_present_cpu in cpu_topology_map
  2017-02-17 11:10                   ` [PATCH v3 1/3] perf: add cpu__max_present_cpu() Jan Stancek
  2017-02-17 11:10                     ` [PATCH v3 2/3] perf: make build_cpu_topology skip offline/absent CPUs Jan Stancek
@ 2017-02-17 11:10                     ` Jan Stancek
  2017-02-17 15:05                       ` Jiri Olsa
  2017-02-21  8:12                       ` [tip:perf/urgent] perf tools: Replace " tip-bot for Jan Stancek
  2017-02-21  8:11                     ` [tip:perf/urgent] perf cpumap: Add cpu__max_present_cpu() tip-bot for Jan Stancek
  2 siblings, 2 replies; 24+ messages in thread
From: Jan Stancek @ 2017-02-17 11:10 UTC (permalink / raw)
  To: linux-kernel; +Cc: peterz, mingo, acme, alexander.shishkin, jolsa, mhiramat

There are 2 problems wrt. cpu_topology_map on systems with sparse CPUs:

1. offline/absent CPUs will have their socket_id and core_id set to -1
   which triggers:
   "socket_id number is too big.You may need to upgrade the perf tool."

2. size of cpu_topology_map (perf_env.cpu[]) is allocated based on
   _SC_NPROCESSORS_CONF, but can be indexed with CPU ids going above.
   Users of perf_env.cpu[] are using CPU id as index. This can lead
   to read beyond what was allocated:
   ==19991== Invalid read of size 4
   ==19991==    at 0x490CEB: check_cpu_topology (topology.c:69)
   ==19991==    by 0x490CEB: test_session_topology (topology.c:106)
   ...

For example:
  _SC_NPROCESSORS_CONF == 16
  available: 2 nodes (0-1)
  node 0 cpus: 0 6 8 10 16 22 24 26
  node 0 size: 12004 MB
  node 0 free: 9470 MB
  node 1 cpus: 1 7 9 11 23 25 27
  node 1 size: 12093 MB
  node 1 free: 9406 MB
  node distances:
  node   0   1
    0:  10  20
    1:  20  10

This patch changes HEADER_NRCPUS.nr_cpus_available from _SC_NPROCESSORS_CONF
to max_present_cpu and updates any user of cpu_topology_map to iterate
with nr_cpus_avail. As consequence HEADER_CPU_TOPOLOGY core_id and
socket_id lists get longer, but maintain compatibility with pre-patch
state - index to cpu_topology_map is CPU id.

 ./perf test 36 -v
36: Session topology                           :
--- start ---
test child forked, pid 22211
templ file: /tmp/perf-test-gmdX5i
CPU 0, core 0, socket 0
CPU 1, core 0, socket 1
CPU 6, core 10, socket 0
CPU 7, core 10, socket 1
CPU 8, core 1, socket 0
CPU 9, core 1, socket 1
CPU 10, core 9, socket 0
CPU 11, core 9, socket 1
CPU 16, core 0, socket 0
CPU 22, core 10, socket 0
CPU 23, core 10, socket 1
CPU 24, core 1, socket 0
CPU 25, core 1, socket 1
CPU 26, core 9, socket 0
CPU 27, core 9, socket 1
test child finished with 0
---- end ----
Session topology: Ok

Signed-off-by: Jan Stancek <jstancek@redhat.com>
---
 tools/perf/builtin-stat.c   |  2 +-
 tools/perf/tests/topology.c |  4 +++-
 tools/perf/util/env.c       |  2 +-
 tools/perf/util/header.c    | 16 +++++-----------
 4 files changed, 10 insertions(+), 14 deletions(-)

diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c
index a02f2e965628..9e2be1c52fbd 100644
--- a/tools/perf/builtin-stat.c
+++ b/tools/perf/builtin-stat.c
@@ -1765,7 +1765,7 @@ static inline int perf_env__get_cpu(struct perf_env *env, struct cpu_map *map, i
 
 	cpu = map->map[idx];
 
-	if (cpu >= env->nr_cpus_online)
+	if (cpu >= env->nr_cpus_avail)
 		return -1;
 
 	return cpu;
diff --git a/tools/perf/tests/topology.c b/tools/perf/tests/topology.c
index 98fe69ac553c..803f893550d6 100644
--- a/tools/perf/tests/topology.c
+++ b/tools/perf/tests/topology.c
@@ -65,7 +65,9 @@ static int check_cpu_topology(char *path, struct cpu_map *map)
 	session = perf_session__new(&file, false, NULL);
 	TEST_ASSERT_VAL("can't get session", session);
 
-	for (i = 0; i < session->header.env.nr_cpus_online; i++) {
+	for (i = 0; i < session->header.env.nr_cpus_avail; i++) {
+		if (!cpu_map__has(map, i))
+			continue;
 		pr_debug("CPU %d, core %d, socket %d\n", i,
 			 session->header.env.cpu[i].core_id,
 			 session->header.env.cpu[i].socket_id);
diff --git a/tools/perf/util/env.c b/tools/perf/util/env.c
index bb964e86b09d..075fc77286bf 100644
--- a/tools/perf/util/env.c
+++ b/tools/perf/util/env.c
@@ -66,7 +66,7 @@ int perf_env__read_cpu_topology_map(struct perf_env *env)
 		return 0;
 
 	if (env->nr_cpus_avail == 0)
-		env->nr_cpus_avail = sysconf(_SC_NPROCESSORS_CONF);
+		env->nr_cpus_avail = cpu__max_present_cpu();
 
 	nr_cpus = env->nr_cpus_avail;
 	if (nr_cpus == -1)
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index ca0f12fb5fd3..3a79a0f10bf6 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -293,11 +293,7 @@ static int write_nrcpus(int fd, struct perf_header *h __maybe_unused,
 	u32 nrc, nra;
 	int ret;
 
-	nr = sysconf(_SC_NPROCESSORS_CONF);
-	if (nr < 0)
-		return -1;
-
-	nrc = (u32)(nr & UINT_MAX);
+	nrc = cpu__max_present_cpu();
 
 	nr = sysconf(_SC_NPROCESSORS_ONLN);
 	if (nr < 0)
@@ -511,9 +507,7 @@ static struct cpu_topo *build_cpu_topology(void)
 	int ret = -1;
 	struct cpu_map *map;
 
-	ncpus = sysconf(_SC_NPROCESSORS_CONF);
-	if (ncpus < 0)
-		return NULL;
+	ncpus = cpu__max_present_cpu();
 
 	/* build online CPU map */
 	map = cpu_map__new(NULL);
@@ -1137,7 +1131,7 @@ static void print_cpu_topology(struct perf_header *ph, int fd __maybe_unused,
 {
 	int nr, i;
 	char *str;
-	int cpu_nr = ph->env.nr_cpus_online;
+	int cpu_nr = ph->env.nr_cpus_avail;
 
 	nr = ph->env.nr_sibling_cores;
 	str = ph->env.sibling_cores;
@@ -1792,7 +1786,7 @@ static int process_cpu_topology(struct perf_file_section *section,
 	u32 nr, i;
 	char *str;
 	struct strbuf sb;
-	int cpu_nr = ph->env.nr_cpus_online;
+	int cpu_nr = ph->env.nr_cpus_avail;
 	u64 size = 0;
 
 	ph->env.cpu = calloc(cpu_nr, sizeof(*ph->env.cpu));
@@ -1873,7 +1867,7 @@ static int process_cpu_topology(struct perf_file_section *section,
 		if (ph->needs_swap)
 			nr = bswap_32(nr);
 
-		if (nr > (u32)cpu_nr) {
+		if (nr != (u32)-1 && nr > (u32)cpu_nr) {
 			pr_debug("socket_id number is too big."
 				 "You may need to upgrade the perf tool.\n");
 			goto free_cpu;
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* Re: [PATCH v3 3/3] perf: replace _SC_NPROCESSORS_CONF with max_present_cpu in cpu_topology_map
  2017-02-17 11:10                     ` [PATCH v3 3/3] perf: replace _SC_NPROCESSORS_CONF with max_present_cpu in cpu_topology_map Jan Stancek
@ 2017-02-17 15:05                       ` Jiri Olsa
  2017-02-21  8:12                       ` [tip:perf/urgent] perf tools: Replace " tip-bot for Jan Stancek
  1 sibling, 0 replies; 24+ messages in thread
From: Jiri Olsa @ 2017-02-17 15:05 UTC (permalink / raw)
  To: Jan Stancek
  Cc: linux-kernel, peterz, mingo, acme, alexander.shishkin, jolsa, mhiramat

On Fri, Feb 17, 2017 at 12:10:26PM +0100, Jan Stancek wrote:

SNIP

> -	int cpu_nr = ph->env.nr_cpus_online;
> +	int cpu_nr = ph->env.nr_cpus_avail;
>  
>  	nr = ph->env.nr_sibling_cores;
>  	str = ph->env.sibling_cores;
> @@ -1792,7 +1786,7 @@ static int process_cpu_topology(struct perf_file_section *section,
>  	u32 nr, i;
>  	char *str;
>  	struct strbuf sb;
> -	int cpu_nr = ph->env.nr_cpus_online;
> +	int cpu_nr = ph->env.nr_cpus_avail;
>  	u64 size = 0;
>  
>  	ph->env.cpu = calloc(cpu_nr, sizeof(*ph->env.cpu));
> @@ -1873,7 +1867,7 @@ static int process_cpu_topology(struct perf_file_section *section,
>  		if (ph->needs_swap)
>  			nr = bswap_32(nr);
>  
> -		if (nr > (u32)cpu_nr) {
> +		if (nr != (u32)-1 && nr > (u32)cpu_nr) {

could you please add a comment here exaplaining the reason
for possible -1 in here.. other than that it's ok,

for the series:

Acked-by: Jiri Olsa <jolsa@kernel.org>

thanks,
jirka

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v3 2/3] perf: make build_cpu_topology skip offline/absent CPUs
  2017-02-17 11:10                     ` [PATCH v3 2/3] perf: make build_cpu_topology skip offline/absent CPUs Jan Stancek
@ 2017-02-17 15:36                       ` Arnaldo Carvalho de Melo
  2017-02-21  8:12                       ` [tip:perf/urgent] perf header: Make " tip-bot for Jan Stancek
  1 sibling, 0 replies; 24+ messages in thread
From: Arnaldo Carvalho de Melo @ 2017-02-17 15:36 UTC (permalink / raw)
  To: Jan Stancek
  Cc: linux-kernel, peterz, mingo, alexander.shishkin, jolsa, mhiramat

Em Fri, Feb 17, 2017 at 12:10:25PM +0100, Jan Stancek escreveu:
> When build_cpu_topo() encounters offline/absent CPUs,
> it fails to find any sysfs entries and returns failure.
> This leads to build_cpu_topology() and write_cpu_topology()
> failing as well.
> 
> Because HEADER_CPU_TOPOLOGY has not been written, read leaves
> cpu_topology_map NULL and we get NULL ptr deref at:
> 
>  ...
>   cmd_test
>    __cmd_test
>     test_and_print
>      run_test
>       test_session_topology
>        check_cpu_topology
> 
> 36: Session topology                           :
> --- start ---

Please add two spaces before tool output, so that we can avoid breaking
scripts looking for --- as separator of patch message from patch
content.

Also look at 'git log file/you/are/modifying' to see the patch log
style, for this one I'm changing to:

perf header: Make build_cpu_topology skip offline/absent CPUs

Thanks,

- Arnaldo

> test child forked, pid 14902
> templ file: /tmp/perf-test-4CKocW
> failed to write feature HEADER_CPU_TOPOLOGY
> perf: Segmentation fault
> Obtained 9 stack frames.
> ./perf(sighandler_dump_stack+0x41) [0x5095f1]
> /lib64/libc.so.6(+0x35250) [0x7f4b7c3c9250]
> ./perf(test_session_topology+0x1db) [0x490ceb]
> ./perf() [0x475b68]
> ./perf(cmd_test+0x5b9) [0x4763c9]
> ./perf() [0x4945a3]
> ./perf(main+0x69f) [0x427e8f]
> /lib64/libc.so.6(__libc_start_main+0xf5) [0x7f4b7c3b5b35]
> ./perf() [0x427fb9]
> test child interrupted
> ---- end ----
> Session topology: FAILED!
> 
> This patch makes build_cpu_topology() skip offline/absent CPUs,
> by checking their presence against cpu_map built from online CPUs.
> 
> Signed-off-by: Jan Stancek <jstancek@redhat.com>
> ---
>  tools/perf/util/header.c | 19 ++++++++++++++++---
>  1 file changed, 16 insertions(+), 3 deletions(-)
> 
> Changes in v3:
> - make ret = -1 by default, so we fail in case build_cpu_topo
>   doesn't run at all
> 
> diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
> index d89c9c7ef4e5..ca0f12fb5fd3 100644
> --- a/tools/perf/util/header.c
> +++ b/tools/perf/util/header.c
> @@ -503,24 +503,31 @@ static void free_cpu_topo(struct cpu_topo *tp)
>  
>  static struct cpu_topo *build_cpu_topology(void)
>  {
> -	struct cpu_topo *tp;
> +	struct cpu_topo *tp = NULL;
>  	void *addr;
>  	u32 nr, i;
>  	size_t sz;
>  	long ncpus;
>  	int ret = -1;
> +	struct cpu_map *map;
>  
>  	ncpus = sysconf(_SC_NPROCESSORS_CONF);
>  	if (ncpus < 0)
>  		return NULL;
>  
> +	/* build online CPU map */
> +	map = cpu_map__new(NULL);
> +	if (map == NULL) {
> +		pr_debug("failed to get system cpumap\n");
> +		return NULL;
> +	}
> +
>  	nr = (u32)(ncpus & UINT_MAX);
>  
>  	sz = nr * sizeof(char *);
> -
>  	addr = calloc(1, sizeof(*tp) + 2 * sz);
>  	if (!addr)
> -		return NULL;
> +		goto out_free;
>  
>  	tp = addr;
>  	tp->cpu_nr = nr;
> @@ -530,10 +537,16 @@ static struct cpu_topo *build_cpu_topology(void)
>  	tp->thread_siblings = addr;
>  
>  	for (i = 0; i < nr; i++) {
> +		if (!cpu_map__has(map, i))
> +			continue;
> +
>  		ret = build_cpu_topo(tp, i);
>  		if (ret < 0)
>  			break;
>  	}
> +
> +out_free:
> +	cpu_map__put(map);
>  	if (ret) {
>  		free_cpu_topo(tp);
>  		tp = NULL;
> -- 
> 1.8.3.1

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [tip:perf/urgent] perf cpumap: Add cpu__max_present_cpu()
  2017-02-17 11:10                   ` [PATCH v3 1/3] perf: add cpu__max_present_cpu() Jan Stancek
  2017-02-17 11:10                     ` [PATCH v3 2/3] perf: make build_cpu_topology skip offline/absent CPUs Jan Stancek
  2017-02-17 11:10                     ` [PATCH v3 3/3] perf: replace _SC_NPROCESSORS_CONF with max_present_cpu in cpu_topology_map Jan Stancek
@ 2017-02-21  8:11                     ` tip-bot for Jan Stancek
  2 siblings, 0 replies; 24+ messages in thread
From: tip-bot for Jan Stancek @ 2017-02-21  8:11 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: mhiramat, mingo, peterz, jstancek, acme, jolsa, linux-kernel,
	alexander.shishkin, hpa, tglx

Commit-ID:  92a7e1278005b6bb3459affc50b2b6e2464e7e7c
Gitweb:     http://git.kernel.org/tip/92a7e1278005b6bb3459affc50b2b6e2464e7e7c
Author:     Jan Stancek <jstancek@redhat.com>
AuthorDate: Fri, 17 Feb 2017 12:10:24 +0100
Committer:  Arnaldo Carvalho de Melo <acme@redhat.com>
CommitDate: Fri, 17 Feb 2017 12:33:05 -0300

perf cpumap: Add cpu__max_present_cpu()

Similar to cpu__max_cpu() (which returns the max possible CPU), returns
the max present CPU.

Signed-off-by: Jan Stancek <jstancek@redhat.com>
Acked-by: Jiri Olsa <jolsa@kernel.org>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Masami Hiramatsu <mhiramat@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: http://lkml.kernel.org/r/8ea4601b5cacc49927235b4ebac424bd6eeccb06.1487146877.git.jstancek@redhat.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
 tools/perf/util/cpumap.c | 22 ++++++++++++++++++++++
 tools/perf/util/cpumap.h |  1 +
 2 files changed, 23 insertions(+)

diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c
index 2c0b522..8c75049 100644
--- a/tools/perf/util/cpumap.c
+++ b/tools/perf/util/cpumap.c
@@ -9,6 +9,7 @@
 #include "asm/bug.h"
 
 static int max_cpu_num;
+static int max_present_cpu_num;
 static int max_node_num;
 static int *cpunode_map;
 
@@ -442,6 +443,7 @@ static void set_max_cpu_num(void)
 
 	/* set up default */
 	max_cpu_num = 4096;
+	max_present_cpu_num = 4096;
 
 	mnt = sysfs__mountpoint();
 	if (!mnt)
@@ -455,6 +457,17 @@ static void set_max_cpu_num(void)
 	}
 
 	ret = get_max_num(path, &max_cpu_num);
+	if (ret)
+		goto out;
+
+	/* get the highest present cpu number for a sparse allocation */
+	ret = snprintf(path, PATH_MAX, "%s/devices/system/cpu/present", mnt);
+	if (ret == PATH_MAX) {
+		pr_err("sysfs path crossed PATH_MAX(%d) size\n", PATH_MAX);
+		goto out;
+	}
+
+	ret = get_max_num(path, &max_present_cpu_num);
 
 out:
 	if (ret)
@@ -505,6 +518,15 @@ int cpu__max_cpu(void)
 	return max_cpu_num;
 }
 
+int cpu__max_present_cpu(void)
+{
+	if (unlikely(!max_present_cpu_num))
+		set_max_cpu_num();
+
+	return max_present_cpu_num;
+}
+
+
 int cpu__get_node(int cpu)
 {
 	if (unlikely(cpunode_map == NULL)) {
diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h
index 06bd689..1a0549a 100644
--- a/tools/perf/util/cpumap.h
+++ b/tools/perf/util/cpumap.h
@@ -62,6 +62,7 @@ int cpu__setup_cpunode_map(void);
 
 int cpu__max_node(void);
 int cpu__max_cpu(void);
+int cpu__max_present_cpu(void);
 int cpu__get_node(int cpu);
 
 int cpu_map__build_map(struct cpu_map *cpus, struct cpu_map **res,

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [tip:perf/urgent] perf header: Make build_cpu_topology skip offline/absent CPUs
  2017-02-17 11:10                     ` [PATCH v3 2/3] perf: make build_cpu_topology skip offline/absent CPUs Jan Stancek
  2017-02-17 15:36                       ` Arnaldo Carvalho de Melo
@ 2017-02-21  8:12                       ` tip-bot for Jan Stancek
  1 sibling, 0 replies; 24+ messages in thread
From: tip-bot for Jan Stancek @ 2017-02-21  8:12 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: linux-kernel, alexander.shishkin, peterz, mhiramat, jolsa, acme,
	mingo, jstancek, hpa, tglx

Commit-ID:  43db2843a4a41cc8cdb6ab696639aeee1f4d5062
Gitweb:     http://git.kernel.org/tip/43db2843a4a41cc8cdb6ab696639aeee1f4d5062
Author:     Jan Stancek <jstancek@redhat.com>
AuthorDate: Fri, 17 Feb 2017 12:10:25 +0100
Committer:  Arnaldo Carvalho de Melo <acme@redhat.com>
CommitDate: Fri, 17 Feb 2017 12:37:04 -0300

perf header: Make build_cpu_topology skip offline/absent CPUs

When build_cpu_topo() encounters offline/absent CPUs, it fails to find any
sysfs entries and returns failure.

This leads to build_cpu_topology() and write_cpu_topology() failing as
well.

Because HEADER_CPU_TOPOLOGY has not been written, read leaves cpu_topology_map
NULL and we get NULL ptr deref at:

  ...
   cmd_test
    __cmd_test
     test_and_print
      run_test
       test_session_topology
        check_cpu_topology

  36: Session topology                           :
  --- start ---
  test child forked, pid 14902
  templ file: /tmp/perf-test-4CKocW
  failed to write feature HEADER_CPU_TOPOLOGY
  perf: Segmentation fault
  Obtained 9 stack frames.
  ./perf(sighandler_dump_stack+0x41) [0x5095f1]
  /lib64/libc.so.6(+0x35250) [0x7f4b7c3c9250]
  ./perf(test_session_topology+0x1db) [0x490ceb]
  ./perf() [0x475b68]
  ./perf(cmd_test+0x5b9) [0x4763c9]
  ./perf() [0x4945a3]
  ./perf(main+0x69f) [0x427e8f]
  /lib64/libc.so.6(__libc_start_main+0xf5) [0x7f4b7c3b5b35]
  ./perf() [0x427fb9]
  test child interrupted
  ---- end ----
  Session topology: FAILED!

This patch makes build_cpu_topology() skip offline/absent CPUs, by checking
their presence against cpu_map built from online CPUs.

Signed-off-by: Jan Stancek <jstancek@redhat.com>
Acked-by: Jiri Olsa <jolsa@kernel.org>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Masami Hiramatsu <mhiramat@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: http://lkml.kernel.org/r/a271b770175524f4961d4903af33798358a4a518.1487146877.git.jstancek@redhat.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
 tools/perf/util/header.c | 19 ++++++++++++++++---
 1 file changed, 16 insertions(+), 3 deletions(-)

diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index 3d12c16..1222f6c 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -505,24 +505,31 @@ static void free_cpu_topo(struct cpu_topo *tp)
 
 static struct cpu_topo *build_cpu_topology(void)
 {
-	struct cpu_topo *tp;
+	struct cpu_topo *tp = NULL;
 	void *addr;
 	u32 nr, i;
 	size_t sz;
 	long ncpus;
 	int ret = -1;
+	struct cpu_map *map;
 
 	ncpus = sysconf(_SC_NPROCESSORS_CONF);
 	if (ncpus < 0)
 		return NULL;
 
+	/* build online CPU map */
+	map = cpu_map__new(NULL);
+	if (map == NULL) {
+		pr_debug("failed to get system cpumap\n");
+		return NULL;
+	}
+
 	nr = (u32)(ncpus & UINT_MAX);
 
 	sz = nr * sizeof(char *);
-
 	addr = calloc(1, sizeof(*tp) + 2 * sz);
 	if (!addr)
-		return NULL;
+		goto out_free;
 
 	tp = addr;
 	tp->cpu_nr = nr;
@@ -532,10 +539,16 @@ static struct cpu_topo *build_cpu_topology(void)
 	tp->thread_siblings = addr;
 
 	for (i = 0; i < nr; i++) {
+		if (!cpu_map__has(map, i))
+			continue;
+
 		ret = build_cpu_topo(tp, i);
 		if (ret < 0)
 			break;
 	}
+
+out_free:
+	cpu_map__put(map);
 	if (ret) {
 		free_cpu_topo(tp);
 		tp = NULL;

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [tip:perf/urgent] perf tools: Replace _SC_NPROCESSORS_CONF with max_present_cpu in cpu_topology_map
  2017-02-17 11:10                     ` [PATCH v3 3/3] perf: replace _SC_NPROCESSORS_CONF with max_present_cpu in cpu_topology_map Jan Stancek
  2017-02-17 15:05                       ` Jiri Olsa
@ 2017-02-21  8:12                       ` tip-bot for Jan Stancek
  1 sibling, 0 replies; 24+ messages in thread
From: tip-bot for Jan Stancek @ 2017-02-21  8:12 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: tglx, peterz, linux-kernel, jstancek, alexander.shishkin, acme,
	jolsa, mingo, mhiramat, hpa

Commit-ID:  da8a58b56c661681f9b2fd2fa59c6da3a5bac8d1
Gitweb:     http://git.kernel.org/tip/da8a58b56c661681f9b2fd2fa59c6da3a5bac8d1
Author:     Jan Stancek <jstancek@redhat.com>
AuthorDate: Fri, 17 Feb 2017 12:10:26 +0100
Committer:  Arnaldo Carvalho de Melo <acme@redhat.com>
CommitDate: Fri, 17 Feb 2017 12:56:35 -0300

perf tools: Replace _SC_NPROCESSORS_CONF with max_present_cpu in cpu_topology_map

There are 2 problems wrt. cpu_topology_map on systems with sparse CPUs:

1. offline/absent CPUs will have their socket_id and core_id set to -1
   which triggers:
   "socket_id number is too big.You may need to upgrade the perf tool."

2. size of cpu_topology_map (perf_env.cpu[]) is allocated based on
   _SC_NPROCESSORS_CONF, but can be indexed with CPU ids going above.
   Users of perf_env.cpu[] are using CPU id as index. This can lead
   to read beyond what was allocated:
   ==19991== Invalid read of size 4
   ==19991==    at 0x490CEB: check_cpu_topology (topology.c:69)
   ==19991==    by 0x490CEB: test_session_topology (topology.c:106)
   ...

For example:
  _SC_NPROCESSORS_CONF == 16
  available: 2 nodes (0-1)
  node 0 cpus: 0 6 8 10 16 22 24 26
  node 0 size: 12004 MB
  node 0 free: 9470 MB
  node 1 cpus: 1 7 9 11 23 25 27
  node 1 size: 12093 MB
  node 1 free: 9406 MB
  node distances:
  node   0   1
    0:  10  20
    1:  20  10

This patch changes HEADER_NRCPUS.nr_cpus_available from _SC_NPROCESSORS_CONF
to max_present_cpu and updates any user of cpu_topology_map to iterate
with nr_cpus_avail.

As a consequence HEADER_CPU_TOPOLOGY core_id and socket_id lists get longer,
but maintain compatibility with pre-patch state - index to cpu_topology_map is
CPU id.

  perf test 36 -v
  36: Session topology                           :
  --- start ---
  test child forked, pid 22211
  templ file: /tmp/perf-test-gmdX5i
  CPU 0, core 0, socket 0
  CPU 1, core 0, socket 1
  CPU 6, core 10, socket 0
  CPU 7, core 10, socket 1
  CPU 8, core 1, socket 0
  CPU 9, core 1, socket 1
  CPU 10, core 9, socket 0
  CPU 11, core 9, socket 1
  CPU 16, core 0, socket 0
  CPU 22, core 10, socket 0
  CPU 23, core 10, socket 1
  CPU 24, core 1, socket 0
  CPU 25, core 1, socket 1
  CPU 26, core 9, socket 0
  CPU 27, core 9, socket 1
  test child finished with 0
  ---- end ----
  Session topology: Ok

Signed-off-by: Jan Stancek <jstancek@redhat.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Masami Hiramatsu <mhiramat@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: http://lkml.kernel.org/r/d7c05c6445fca74a8442c2c73cfffd349c52c44f.1487146877.git.jstancek@redhat.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
 tools/perf/builtin-stat.c   |  2 +-
 tools/perf/tests/topology.c |  4 +++-
 tools/perf/util/env.c       |  2 +-
 tools/perf/util/header.c    | 16 +++++-----------
 4 files changed, 10 insertions(+), 14 deletions(-)

diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c
index f287191..ca27a8a 100644
--- a/tools/perf/builtin-stat.c
+++ b/tools/perf/builtin-stat.c
@@ -1765,7 +1765,7 @@ static inline int perf_env__get_cpu(struct perf_env *env, struct cpu_map *map, i
 
 	cpu = map->map[idx];
 
-	if (cpu >= env->nr_cpus_online)
+	if (cpu >= env->nr_cpus_avail)
 		return -1;
 
 	return cpu;
diff --git a/tools/perf/tests/topology.c b/tools/perf/tests/topology.c
index 98fe69a..803f893 100644
--- a/tools/perf/tests/topology.c
+++ b/tools/perf/tests/topology.c
@@ -65,7 +65,9 @@ static int check_cpu_topology(char *path, struct cpu_map *map)
 	session = perf_session__new(&file, false, NULL);
 	TEST_ASSERT_VAL("can't get session", session);
 
-	for (i = 0; i < session->header.env.nr_cpus_online; i++) {
+	for (i = 0; i < session->header.env.nr_cpus_avail; i++) {
+		if (!cpu_map__has(map, i))
+			continue;
 		pr_debug("CPU %d, core %d, socket %d\n", i,
 			 session->header.env.cpu[i].core_id,
 			 session->header.env.cpu[i].socket_id);
diff --git a/tools/perf/util/env.c b/tools/perf/util/env.c
index bb964e8..075fc77 100644
--- a/tools/perf/util/env.c
+++ b/tools/perf/util/env.c
@@ -66,7 +66,7 @@ int perf_env__read_cpu_topology_map(struct perf_env *env)
 		return 0;
 
 	if (env->nr_cpus_avail == 0)
-		env->nr_cpus_avail = sysconf(_SC_NPROCESSORS_CONF);
+		env->nr_cpus_avail = cpu__max_present_cpu();
 
 	nr_cpus = env->nr_cpus_avail;
 	if (nr_cpus == -1)
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index 1222f6c..05714d5 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -295,11 +295,7 @@ static int write_nrcpus(int fd, struct perf_header *h __maybe_unused,
 	u32 nrc, nra;
 	int ret;
 
-	nr = sysconf(_SC_NPROCESSORS_CONF);
-	if (nr < 0)
-		return -1;
-
-	nrc = (u32)(nr & UINT_MAX);
+	nrc = cpu__max_present_cpu();
 
 	nr = sysconf(_SC_NPROCESSORS_ONLN);
 	if (nr < 0)
@@ -513,9 +509,7 @@ static struct cpu_topo *build_cpu_topology(void)
 	int ret = -1;
 	struct cpu_map *map;
 
-	ncpus = sysconf(_SC_NPROCESSORS_CONF);
-	if (ncpus < 0)
-		return NULL;
+	ncpus = cpu__max_present_cpu();
 
 	/* build online CPU map */
 	map = cpu_map__new(NULL);
@@ -1139,7 +1133,7 @@ static void print_cpu_topology(struct perf_header *ph, int fd __maybe_unused,
 {
 	int nr, i;
 	char *str;
-	int cpu_nr = ph->env.nr_cpus_online;
+	int cpu_nr = ph->env.nr_cpus_avail;
 
 	nr = ph->env.nr_sibling_cores;
 	str = ph->env.sibling_cores;
@@ -1794,7 +1788,7 @@ static int process_cpu_topology(struct perf_file_section *section,
 	u32 nr, i;
 	char *str;
 	struct strbuf sb;
-	int cpu_nr = ph->env.nr_cpus_online;
+	int cpu_nr = ph->env.nr_cpus_avail;
 	u64 size = 0;
 
 	ph->env.cpu = calloc(cpu_nr, sizeof(*ph->env.cpu));
@@ -1875,7 +1869,7 @@ static int process_cpu_topology(struct perf_file_section *section,
 		if (ph->needs_swap)
 			nr = bswap_32(nr);
 
-		if (nr > (u32)cpu_nr) {
+		if (nr != (u32)-1 && nr > (u32)cpu_nr) {
 			pr_debug("socket_id number is too big."
 				 "You may need to upgrade the perf tool.\n");
 			goto free_cpu;

^ permalink raw reply related	[flat|nested] 24+ messages in thread

end of thread, other threads:[~2017-02-21  8:13 UTC | newest]

Thread overview: 24+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-01-30 16:53 [PATCH] perf: fix topology test on systems with sparse CPUs Jan Stancek
2017-01-30 18:49 ` Jiri Olsa
2017-01-30 19:29   ` Jan Stancek
2017-01-31 16:03   ` Jan Stancek
2017-02-02 11:29     ` Jiri Olsa
2017-02-02 12:06       ` Jan Stancek
2017-02-02 13:01         ` Jiri Olsa
2017-02-13 15:34           ` [PATCH v2 1/3] perf: add cpu__max_present_cpu() Jan Stancek
2017-02-13 15:34             ` [PATCH v2 2/3] perf: make build_cpu_topology skip offline/absent CPUs Jan Stancek
2017-02-14 11:01               ` Jiri Olsa
2017-02-15  8:48                 ` Jan Stancek
2017-02-17 11:10                   ` [PATCH v3 1/3] perf: add cpu__max_present_cpu() Jan Stancek
2017-02-17 11:10                     ` [PATCH v3 2/3] perf: make build_cpu_topology skip offline/absent CPUs Jan Stancek
2017-02-17 15:36                       ` Arnaldo Carvalho de Melo
2017-02-21  8:12                       ` [tip:perf/urgent] perf header: Make " tip-bot for Jan Stancek
2017-02-17 11:10                     ` [PATCH v3 3/3] perf: replace _SC_NPROCESSORS_CONF with max_present_cpu in cpu_topology_map Jan Stancek
2017-02-17 15:05                       ` Jiri Olsa
2017-02-21  8:12                       ` [tip:perf/urgent] perf tools: Replace " tip-bot for Jan Stancek
2017-02-21  8:11                     ` [tip:perf/urgent] perf cpumap: Add cpu__max_present_cpu() tip-bot for Jan Stancek
2017-02-13 15:34             ` [PATCH v2 3/3] perf: replace _SC_NPROCESSORS_CONF with max_present_cpu in cpu_topology_map Jan Stancek
2017-02-14 11:17             ` [PATCH v2 1/3] perf: add cpu__max_present_cpu() Jiri Olsa
2017-02-02 11:29     ` [PATCH] perf: fix topology test on systems with sparse CPUs Jiri Olsa
2017-01-30 18:49 ` Jiri Olsa
2017-01-30 18:49 ` Jiri Olsa

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).