Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 22 Aug 2017 00:10:15 +0000 (UTC)
From:      Conrad Meyer <cem@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r322776 - in head/sys: kern sys x86/x86
Message-ID:  <201708220010.v7M0AFV4084597@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: cem
Date: Tue Aug 22 00:10:15 2017
New Revision: 322776
URL: https://svnweb.freebsd.org/changeset/base/322776

Log:
  subr_smp: Clean up topology analysis, add additional layers
  
  Rather than repeatedly nesting loops, separate concerns with a single loop
  per call stack level.  Use a table to drive the recursive routine.  Handle
  missing topology layers more gracefully (infer a single unit).
  
  Analyze some additional optional layers which may be present on e.g. AMD Zen
  systems (groups, aka dies, per package; and cachegroups, aka CCXes, per
  group).
  
  Display that additional information in the boot-time topology information,
  when it is relevent (non-one).
  
  Reviewed by:	markj@, mjoras@ (earlier version)
  Sponsored by:	Dell EMC Isilon
  Differential Revision:	https://reviews.freebsd.org/D12019

Modified:
  head/sys/kern/subr_smp.c
  head/sys/sys/smp.h
  head/sys/x86/x86/mp_x86.c

Modified: head/sys/kern/subr_smp.c
==============================================================================
--- head/sys/kern/subr_smp.c	Mon Aug 21 22:26:49 2017	(r322775)
+++ head/sys/kern/subr_smp.c	Tue Aug 22 00:10:15 2017	(r322776)
@@ -993,7 +993,7 @@ topo_next_node(struct topo_node *top, struct topo_node
 	if ((next = TAILQ_NEXT(node, siblings)) != NULL)
 		return (next);
 
-	while ((node = node->parent) != top)
+	while (node != top && (node = node->parent) != top)
 		if ((next = TAILQ_NEXT(node, siblings)) != NULL)
 			return (next);
 
@@ -1012,7 +1012,7 @@ topo_next_nonchild_node(struct topo_node *top, struct 
 	if ((next = TAILQ_NEXT(node, siblings)) != NULL)
 		return (next);
 
-	while ((node = node->parent) != top)
+	while (node != top && (node = node->parent) != top)
 		if ((next = TAILQ_NEXT(node, siblings)) != NULL)
 			return (next);
 
@@ -1044,105 +1044,99 @@ topo_set_pu_id(struct topo_node *node, cpuid_t id)
 	}
 }
 
-/*
- * Check if the topology is uniform, that is, each package has the same number
- * of cores in it and each core has the same number of threads (logical
- * processors) in it.  If so, calculate the number of package, the number of
- * cores per package and the number of logical processors per core.
- * 'all' parameter tells whether to include administratively disabled logical
- * processors into the analysis.
- */
-int
-topo_analyze(struct topo_node *topo_root, int all,
-    int *pkg_count, int *cores_per_pkg, int *thrs_per_core)
+static struct topology_spec {
+	topo_node_type	type;
+	bool		match_subtype;
+	uintptr_t	subtype;
+} topology_level_table[TOPO_LEVEL_COUNT] = {
+	[TOPO_LEVEL_PKG] = { .type = TOPO_TYPE_PKG, },
+	[TOPO_LEVEL_GROUP] = { .type = TOPO_TYPE_GROUP, },
+	[TOPO_LEVEL_CACHEGROUP] = {
+		.type = TOPO_TYPE_CACHE,
+		.match_subtype = true,
+		.subtype = CG_SHARE_L3,
+	},
+	[TOPO_LEVEL_CORE] = { .type = TOPO_TYPE_CORE, },
+	[TOPO_LEVEL_THREAD] = { .type = TOPO_TYPE_PU, },
+};
+
+static bool
+topo_analyze_table(struct topo_node *root, int all, enum topo_level level,
+    struct topo_analysis *results)
 {
-	struct topo_node *pkg_node;
-	struct topo_node *core_node;
-	struct topo_node *pu_node;
-	int thrs_per_pkg;
-	int cpp_counter;
-	int tpc_counter;
-	int tpp_counter;
+	struct topology_spec *spec;
+	struct topo_node *node;
+	int count;
 
-	*pkg_count = 0;
-	*cores_per_pkg = -1;
-	*thrs_per_core = -1;
-	thrs_per_pkg = -1;
-	pkg_node = topo_root;
-	while (pkg_node != NULL) {
-		if (pkg_node->type != TOPO_TYPE_PKG) {
-			pkg_node = topo_next_node(topo_root, pkg_node);
+	if (level >= TOPO_LEVEL_COUNT)
+		return (true);
+
+	spec = &topology_level_table[level];
+	count = 0;
+	node = topo_next_node(root, root);
+
+	while (node != NULL) {
+		if (node->type != spec->type ||
+		    (spec->match_subtype && node->subtype != spec->subtype)) {
+			node = topo_next_node(root, node);
 			continue;
 		}
-		if (!all && CPU_EMPTY(&pkg_node->cpuset)) {
-			pkg_node = topo_next_nonchild_node(topo_root, pkg_node);
+		if (!all && CPU_EMPTY(&node->cpuset)) {
+			node = topo_next_nonchild_node(root, node);
 			continue;
 		}
 
-		(*pkg_count)++;
+		count++;
 
-		cpp_counter = 0;
-		tpp_counter = 0;
-		core_node = pkg_node;
-		while (core_node != NULL) {
-			if (core_node->type == TOPO_TYPE_CORE) {
-				if (!all && CPU_EMPTY(&core_node->cpuset)) {
-					core_node =
-					    topo_next_nonchild_node(pkg_node,
-					        core_node);
-					continue;
-				}
+		if (!topo_analyze_table(node, all, level + 1, results))
+			return (false);
 
-				cpp_counter++;
+		node = topo_next_nonchild_node(root, node);
+	}
 
-				tpc_counter = 0;
-				pu_node = core_node;
-				while (pu_node != NULL) {
-					if (pu_node->type == TOPO_TYPE_PU &&
-					    (all || !CPU_EMPTY(&pu_node->cpuset)))
-						tpc_counter++;
-					pu_node = topo_next_node(core_node,
-					    pu_node);
-				}
+	/* No explicit subgroups is essentially one subgroup. */
+	if (count == 0) {
+		count = 1;
 
-				if (*thrs_per_core == -1)
-					*thrs_per_core = tpc_counter;
-				else if (*thrs_per_core != tpc_counter)
-					return (0);
+		if (!topo_analyze_table(root, all, level + 1, results))
+			return (false);
+	}
 
-				core_node = topo_next_nonchild_node(pkg_node,
-				    core_node);
-			} else {
-				/* PU node directly under PKG. */
-				if (core_node->type == TOPO_TYPE_PU &&
-			           (all || !CPU_EMPTY(&core_node->cpuset)))
-					tpp_counter++;
-				core_node = topo_next_node(pkg_node,
-				    core_node);
-			}
-		}
+	if (results->entities[level] == -1)
+		results->entities[level] = count;
+	else if (results->entities[level] != count)
+		return (false);
 
-		if (*cores_per_pkg == -1)
-			*cores_per_pkg = cpp_counter;
-		else if (*cores_per_pkg != cpp_counter)
-			return (0);
-		if (thrs_per_pkg == -1)
-			thrs_per_pkg = tpp_counter;
-		else if (thrs_per_pkg != tpp_counter)
-			return (0);
+	return (true);
+}
 
-		pkg_node = topo_next_nonchild_node(topo_root, pkg_node);
-	}
+/*
+ * Check if the topology is uniform, that is, each package has the same number
+ * of cores in it and each core has the same number of threads (logical
+ * processors) in it.  If so, calculate the number of packages, the number of
+ * groups per package, the number of cachegroups per group, and the number of
+ * logical processors per cachegroup.  'all' parameter tells whether to include
+ * administratively disabled logical processors into the analysis.
+ */
+int
+topo_analyze(struct topo_node *topo_root, int all,
+    struct topo_analysis *results)
+{
 
-	KASSERT(*pkg_count > 0,
+	results->entities[TOPO_LEVEL_PKG] = -1;
+	results->entities[TOPO_LEVEL_CORE] = -1;
+	results->entities[TOPO_LEVEL_THREAD] = -1;
+	results->entities[TOPO_LEVEL_GROUP] = -1;
+	results->entities[TOPO_LEVEL_CACHEGROUP] = -1;
+
+	if (!topo_analyze_table(topo_root, all, TOPO_LEVEL_PKG, results))
+		return (0);
+
+	KASSERT(results->entities[TOPO_LEVEL_PKG] > 0,
 		("bug in topology or analysis"));
-	if (*cores_per_pkg == 0) {
-		KASSERT(*thrs_per_core == -1 && thrs_per_pkg > 0,
-			("bug in topology or analysis"));
-		*thrs_per_core = thrs_per_pkg;
-	}
 
 	return (1);
 }
+
 #endif /* SMP */
 

Modified: head/sys/sys/smp.h
==============================================================================
--- head/sys/sys/smp.h	Mon Aug 21 22:26:49 2017	(r322775)
+++ head/sys/sys/smp.h	Tue Aug 22 00:10:15 2017	(r322776)
@@ -120,8 +120,25 @@ struct topo_node * topo_next_node(struct topo_node *to
 struct topo_node * topo_next_nonchild_node(struct topo_node *top,
     struct topo_node *node);
 void topo_set_pu_id(struct topo_node *node, cpuid_t id);
-int topo_analyze(struct topo_node *topo_root, int all, int *pkg_count,
-    int *cores_per_pkg, int *thrs_per_core);
+
+enum topo_level {
+	TOPO_LEVEL_PKG = 0,
+	/*
+	 * Some systems have useful sub-package core organizations.  On these,
+	 * a package has one or more subgroups.  Each subgroup contains one or
+	 * more cache groups (cores that share a last level cache).
+	 */
+	TOPO_LEVEL_GROUP,
+	TOPO_LEVEL_CACHEGROUP,
+	TOPO_LEVEL_CORE,
+	TOPO_LEVEL_THREAD,
+	TOPO_LEVEL_COUNT	/* Must be last */
+};
+struct topo_analysis {
+	int entities[TOPO_LEVEL_COUNT];
+};
+int topo_analyze(struct topo_node *topo_root, int all,
+    struct topo_analysis *results);
 
 #define	TOPO_FOREACH(i, root)	\
 	for (i = root; i != NULL; i = topo_next_node(root, i))

Modified: head/sys/x86/x86/mp_x86.c
==============================================================================
--- head/sys/x86/x86/mp_x86.c	Mon Aug 21 22:26:49 2017	(r322775)
+++ head/sys/x86/x86/mp_x86.c	Tue Aug 22 00:10:15 2017	(r322776)
@@ -662,18 +662,23 @@ cpu_mp_announce(void)
 {
 	struct topo_node *node;
 	const char *hyperthread;
-	int pkg_count;
-	int cores_per_pkg;
-	int thrs_per_core;
+	struct topo_analysis topology;
 
 	printf("FreeBSD/SMP: ");
-	if (topo_analyze(&topo_root, 1, &pkg_count,
-	    &cores_per_pkg, &thrs_per_core)) {
-		printf("%d package(s)", pkg_count);
-		if (cores_per_pkg > 0)
-			printf(" x %d core(s)", cores_per_pkg);
-		if (thrs_per_core > 1)
-		    printf(" x %d hardware threads", thrs_per_core);
+	if (topo_analyze(&topo_root, 1, &topology)) {
+		printf("%d package(s)", topology.entities[TOPO_LEVEL_PKG]);
+		if (topology.entities[TOPO_LEVEL_GROUP] > 1)
+			printf(" x %d groups",
+			    topology.entities[TOPO_LEVEL_GROUP]);
+		if (topology.entities[TOPO_LEVEL_CACHEGROUP] > 1)
+			printf(" x %d cache groups",
+			    topology.entities[TOPO_LEVEL_CACHEGROUP]);
+		if (topology.entities[TOPO_LEVEL_CORE] > 0)
+			printf(" x %d core(s)",
+			    topology.entities[TOPO_LEVEL_CORE]);
+		if (topology.entities[TOPO_LEVEL_THREAD] > 1)
+			printf(" x %d hardware threads",
+			    topology.entities[TOPO_LEVEL_THREAD]);
 	} else {
 		printf("Non-uniform topology");
 	}
@@ -681,13 +686,21 @@ cpu_mp_announce(void)
 
 	if (disabled_cpus) {
 		printf("FreeBSD/SMP Online: ");
-		if (topo_analyze(&topo_root, 0, &pkg_count,
-		    &cores_per_pkg, &thrs_per_core)) {
-			printf("%d package(s)", pkg_count);
-			if (cores_per_pkg > 0)
-				printf(" x %d core(s)", cores_per_pkg);
-			if (thrs_per_core > 1)
-			    printf(" x %d hardware threads", thrs_per_core);
+		if (topo_analyze(&topo_root, 0, &topology)) {
+			printf("%d package(s)",
+			    topology.entities[TOPO_LEVEL_PKG]);
+			if (topology.entities[TOPO_LEVEL_GROUP] > 1)
+				printf(" x %d groups",
+				    topology.entities[TOPO_LEVEL_GROUP]);
+			if (topology.entities[TOPO_LEVEL_CACHEGROUP] > 1)
+				printf(" x %d cache groups",
+				    topology.entities[TOPO_LEVEL_CACHEGROUP]);
+			if (topology.entities[TOPO_LEVEL_CORE] > 0)
+				printf(" x %d core(s)",
+				    topology.entities[TOPO_LEVEL_CORE]);
+			if (topology.entities[TOPO_LEVEL_THREAD] > 1)
+				printf(" x %d hardware threads",
+				    topology.entities[TOPO_LEVEL_THREAD]);
 		} else {
 			printf("Non-uniform topology");
 		}



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201708220010.v7M0AFV4084597>