Skip to content

Commit

Permalink
API: add a "dont_merge" group attribute
Browse files Browse the repository at this point in the history
This tells the code not to ever merge that group with structurally-identical
parent or children.

This is useful for Groups implementing new "types" that cannot be backported
to stable releases. New types won't be merged by default, but Groups would.

Requested by Intel for Die objects.

This doesn't break the ABI because the attribute structure has always been
calloc'ed, which means this attribute was "0", which matches the default
"merge group" behavior.

Signed-off-by: Brice Goglin <Brice.Goglin@inria.fr>
  • Loading branch information
bgoglin committed May 28, 2019
1 parent 676e745 commit f9903a4
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 17 deletions.
2 changes: 2 additions & 0 deletions NEWS
Expand Up @@ -40,6 +40,8 @@ Version 2.1.0
administrative restrictions such as Linux Cgroups.
- Add disallowed_pu and disallowed_numa bits to the discovery support
structure.
+ Group objects have a new "dont_merge" attribute to prevent them from
being automatically merged with identical parent or children.
* Backends
+ Add support for Intel v2 Extended Topology Enumeration in the x86 backend.
Tiles, Modules and Dies are exposed as Groups for now.
Expand Down
5 changes: 4 additions & 1 deletion doc/hwloc.doxy
Expand Up @@ -3106,9 +3106,12 @@ It means that a Group containing a single child is merged
into that child.
And a Group is merged into its parent if it is its only child.
For instance a Windows processor group containing a single NUMA node
would be merged with that NUMA node since it already contains the
would be merged with that NUMA node since it already contains the
relevant hierarchy information.

When inserting a custom Group with hwloc_hwloc_topology_insert_group_object(),
this merging may be disabled by setting its <tt>dont_merge</tt> attribute.


\subsection faq_asymmetric What happens if my topology is asymmetric?

Expand Down
1 change: 1 addition & 0 deletions hwloc/hwloc2.dtd
Expand Up @@ -30,6 +30,7 @@
<!ATTLIST object depth CDATA "-1" >
<!ATTLIST object kind CDATA "0" >
<!ATTLIST object subkind CDATA "0" >
<!ATTLIST object dont_merge CDATA "0" >
<!ATTLIST object bridge_type CDATA "" >
<!ATTLIST object bridge_pci CDATA "" >
<!ATTLIST object pci_busid CDATA "" >
Expand Down
13 changes: 13 additions & 0 deletions hwloc/topology-xml.c
Expand Up @@ -238,6 +238,15 @@ hwloc__xml_import_object_attr(struct hwloc_topology *topology,
state->global->msgprefix);
}

else if (!strcmp(name, "dont_merge")) {
unsigned long lvalue = strtoul(value, NULL, 10);
if (obj->type == HWLOC_OBJ_GROUP)
obj->attr->group.dont_merge = lvalue;
else if (hwloc__xml_verbose())
fprintf(stderr, "%s: ignoring dont_merge attribute for non-group object type\n",
state->global->msgprefix);
}

else if (!strcmp(name, "pci_busid")) {
switch (obj->type) {
case HWLOC_OBJ_PCI_DEVICE:
Expand Down Expand Up @@ -2058,11 +2067,15 @@ hwloc__xml_export_object_contents (hwloc__xml_export_state_t state, hwloc_topolo
if (v1export) {
sprintf(tmp, "%u", obj->attr->group.depth);
state->new_prop(state, "depth", tmp);
if (obj->attr->group.dont_merge)
state->new_prop(state, "dont_merge", "1");
} else {
sprintf(tmp, "%u", obj->attr->group.kind);
state->new_prop(state, "kind", tmp);
sprintf(tmp, "%u", obj->attr->group.subkind);
state->new_prop(state, "subkind", tmp);
if (obj->attr->group.dont_merge)
state->new_prop(state, "dont_merge", "1");
}
break;
case HWLOC_OBJ_BRIDGE:
Expand Down
66 changes: 50 additions & 16 deletions hwloc/topology.c
Expand Up @@ -1268,8 +1268,30 @@ merge_insert_equal(hwloc_obj_t new, hwloc_obj_t old)
static __hwloc_inline hwloc_obj_t
hwloc__insert_try_merge_group(hwloc_obj_t old, hwloc_obj_t new)
{
if (new->type == HWLOC_OBJ_GROUP) {
/* Groups are ignored keep_structure or always. Non-ignored Groups isn't possible (asserted in topology_check()). */
if (new->type == HWLOC_OBJ_GROUP && old->type == HWLOC_OBJ_GROUP) {
/* which group do we keep? */
if (new->attr->group.dont_merge) {
if (old->attr->group.dont_merge)
/* nobody wants to be merged */
return NULL;

/* keep the new one, it doesn't want to be merged */
hwloc_replace_linked_object(old, new);
return new;

} else {
if (old->attr->group.dont_merge)
/* keep the old one, it doesn't want to be merged */
return old;

/* compare subkinds to decice who to keep */
if (new->attr->group.kind < old->attr->group.kind)
hwloc_replace_linked_object(old, new);
return old;
}
}

if (new->type == HWLOC_OBJ_GROUP && !new->attr->group.dont_merge) {

if (old->type == HWLOC_OBJ_PU && new->attr->group.kind == HWLOC_GROUP_KIND_MEMORY)
/* Never merge Memory groups with PU, we don't want to attach Memory under PU */
Expand All @@ -1278,18 +1300,9 @@ hwloc__insert_try_merge_group(hwloc_obj_t old, hwloc_obj_t new)
/* Remove the Group now. The normal ignore code path wouldn't tell us whether the Group was removed or not,
* while some callers need to know (at least hwloc_topology_insert_group()).
*/

/* If merging two groups, keep the smallest kind.
* Replace the existing Group with the new Group contents
* and let the caller free the new Group.
*/
if (old->type == HWLOC_OBJ_GROUP
&& (new->attr->group.kind < old->attr->group.kind))
hwloc_replace_linked_object(old, new);

return old;

} else if (old->type == HWLOC_OBJ_GROUP) {
} else if (old->type == HWLOC_OBJ_GROUP && !old->attr->group.dont_merge) {

if (new->type == HWLOC_OBJ_PU && old->attr->group.kind == HWLOC_GROUP_KIND_MEMORY)
/* Never merge Memory groups with PU, we don't want to attach Memory under PU */
Expand All @@ -1300,9 +1313,11 @@ hwloc__insert_try_merge_group(hwloc_obj_t old, hwloc_obj_t new)
*/
hwloc_replace_linked_object(old, new);
return old;
}

return NULL;
} else {
/* cannot merge */
return NULL;
}
}

/*
Expand Down Expand Up @@ -2291,6 +2306,19 @@ hwloc_reset_normal_type_depths(hwloc_topology_t topology)
/* type contiguity is asserted in topology_check() */
}

static int
hwloc_dont_merge_group_level(hwloc_topology_t topology, unsigned i)
{
unsigned j;

/* Don't merge some groups in that level? */
for(j=0; j<topology->level_nbobjects[i]; j++)
if (topology->levels[i][j]->attr->group.dont_merge)
return 1;

return 0;
}

/* compare i-th and i-1-th levels structure */
static int
hwloc_compare_levels_structure(hwloc_topology_t topology, unsigned i)
Expand Down Expand Up @@ -2328,12 +2356,18 @@ hwloc_filter_levels_keep_structure(hwloc_topology_t topology)
hwloc_obj_type_t type2 = obj2->type;

/* Check whether parents and/or children can be replaced */
if (topology->type_filter[type1] == HWLOC_TYPE_FILTER_KEEP_STRUCTURE)
if (topology->type_filter[type1] == HWLOC_TYPE_FILTER_KEEP_STRUCTURE) {
/* Parents can be ignored in favor of children. */
replaceparent = 1;
if (topology->type_filter[type2] == HWLOC_TYPE_FILTER_KEEP_STRUCTURE)
if (type1 == HWLOC_OBJ_GROUP && hwloc_dont_merge_group_level(topology, i-1))
replaceparent = 0;
}
if (topology->type_filter[type2] == HWLOC_TYPE_FILTER_KEEP_STRUCTURE) {
/* Children can be ignored in favor of parents. */
replacechild = 1;
if (type1 == HWLOC_OBJ_GROUP && hwloc_dont_merge_group_level(topology, i))
replacechild = 0;
}
if (!replacechild && !replaceparent)
/* no ignoring */
continue;
Expand Down
4 changes: 4 additions & 0 deletions include/hwloc.h
Expand Up @@ -604,6 +604,7 @@ union hwloc_obj_attr_u {
* It may change if intermediate Group objects are added. */
unsigned kind; /**< \brief Internally-used kind of group. */
unsigned subkind; /**< \brief Internally-used subkind to distinguish different levels of groups with same kind */
unsigned char dont_merge; /**< \brief Flag preventing groups from being automatically merged with identical parent or children. */
} group;
/** \brief PCI Device specific Object Attributes */
struct hwloc_pcidev_attr_s {
Expand Down Expand Up @@ -2306,6 +2307,9 @@ HWLOC_DECLSPEC hwloc_obj_t hwloc_topology_alloc_group_object(hwloc_topology_t to
* the final location of the Group in the topology.
* Then the object can be passed to this function for actual insertion in the topology.
*
* The group \p dont_merge attribute may be set to prevent the core from
* ever merging this object with another object hierarchically-identical.
*
* Either the cpuset or nodeset field (or both, if compatible) must be set
* to a non-empty bitmap. The complete_cpuset or complete_nodeset may be set
* instead if inserting with respect to the complete topology
Expand Down

0 comments on commit f9903a4

Please sign in to comment.