Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-09-24 09:12:47

0001 /*
0002  * SPDX-License-Identifier: BSD-3-Clause
0003  * Copyright © 2009 CNRS
0004  * Copyright © 2009-2024 Inria.  All rights reserved.
0005  * Copyright © 2009-2012 Université Bordeaux
0006  * Copyright © 2009-2010 Cisco Systems, Inc.  All rights reserved.
0007  * See COPYING in top-level directory.
0008  */
0009 
0010 /** \file
0011  * \brief High-level hwloc traversal helpers.
0012  */
0013 
0014 #ifndef HWLOC_HELPER_H
0015 #define HWLOC_HELPER_H
0016 
0017 #ifndef HWLOC_H
0018 #error Please include the main hwloc.h instead
0019 #endif
0020 
0021 #include <stdlib.h>
0022 #include <errno.h>
0023 
0024 
0025 #ifdef __cplusplus
0026 extern "C" {
0027 #endif
0028 
0029 
0030 /** \defgroup hwlocality_helper_types Kinds of object Type
0031  * @{
0032  *
0033  * Each object type is
0034  * either Normal (i.e. hwloc_obj_type_is_normal() returns 1),
0035  * or Memory (i.e. hwloc_obj_type_is_memory() returns 1)
0036  * or I/O (i.e. hwloc_obj_type_is_io() returns 1)
0037  * or Misc (i.e. equal to ::HWLOC_OBJ_MISC).
0038  * It cannot be of more than one of these kinds.
0039  *
0040  * See also Object Kind in \ref termsanddefs.
0041  */
0042 
0043 /** \brief Check whether an object type is Normal.
0044  *
0045  * Normal objects are objects of the main CPU hierarchy
0046  * (Machine, Package, Core, PU, CPU caches, etc.),
0047  * but they are not NUMA nodes, I/O devices or Misc objects.
0048  *
0049  * They are attached to parent as Normal children,
0050  * not as Memory, I/O or Misc children.
0051  *
0052  * \return 1 if an object of type \p type is a Normal object, 0 otherwise.
0053  */
0054 HWLOC_DECLSPEC int
0055 hwloc_obj_type_is_normal(hwloc_obj_type_t type);
0056 
0057 /** \brief Check whether an object type is I/O.
0058  *
0059  * I/O objects are objects attached to their parents
0060  * in the I/O children list.
0061  * This current includes Bridges, PCI and OS devices.
0062  *
0063  * \return 1 if an object of type \p type is a I/O object, 0 otherwise.
0064  */
0065 HWLOC_DECLSPEC int
0066 hwloc_obj_type_is_io(hwloc_obj_type_t type);
0067 
0068 /** \brief Check whether an object type is Memory.
0069  *
0070  * Memory objects are objects attached to their parents
0071  * in the Memory children list.
0072  * This current includes NUMA nodes and Memory-side caches.
0073  *
0074  * \return 1 if an object of type \p type is a Memory object, 0 otherwise.
0075  */
0076 HWLOC_DECLSPEC int
0077 hwloc_obj_type_is_memory(hwloc_obj_type_t type);
0078 
0079 /** \brief Check whether an object type is a CPU Cache (Data, Unified or Instruction).
0080  *
0081  * Memory-side caches are not CPU caches.
0082  *
0083  * \return 1 if an object of type \p type is a Cache, 0 otherwise.
0084  */
0085 HWLOC_DECLSPEC int
0086 hwloc_obj_type_is_cache(hwloc_obj_type_t type);
0087 
0088 /** \brief Check whether an object type is a CPU Data or Unified Cache.
0089  *
0090  * Memory-side caches are not CPU caches.
0091  *
0092  * \return 1 if an object of type \p type is a CPU Data or Unified Cache, 0 otherwise.
0093  */
0094 HWLOC_DECLSPEC int
0095 hwloc_obj_type_is_dcache(hwloc_obj_type_t type);
0096 
0097 /** \brief Check whether an object type is a CPU Instruction Cache,
0098  *
0099  * Memory-side caches are not CPU caches.
0100  *
0101  * \return 1 if an object of type \p type is a CPU Instruction Cache, 0 otherwise.
0102  */
0103 HWLOC_DECLSPEC int
0104 hwloc_obj_type_is_icache(hwloc_obj_type_t type);
0105 
0106 /** @} */
0107 
0108 
0109 
0110 /** \defgroup hwlocality_helper_find_inside Finding Objects inside a CPU set
0111  * @{
0112  */
0113 
0114 /** \brief Get the first largest object included in the given cpuset \p set.
0115  *
0116  * \return the first object that is included in \p set and whose parent is not.
0117  * \return \c NULL if no such object exists.
0118  *
0119  * This is convenient for iterating over all largest objects within a CPU set
0120  * by doing a loop getting the first largest object and clearing its CPU set
0121  * from the remaining CPU set.
0122  */
0123 static __hwloc_inline hwloc_obj_t
0124 hwloc_get_first_largest_obj_inside_cpuset(hwloc_topology_t topology, hwloc_const_cpuset_t set)
0125 {
0126   hwloc_obj_t obj = hwloc_get_root_obj(topology);
0127   if (!hwloc_bitmap_intersects(obj->cpuset, set))
0128     return NULL;
0129   while (!hwloc_bitmap_isincluded(obj->cpuset, set)) {
0130     /* while the object intersects without being included, look at its children */
0131     hwloc_obj_t child = obj->first_child;
0132     while (child) {
0133       if (hwloc_bitmap_intersects(child->cpuset, set))
0134     break;
0135       child = child->next_sibling;
0136     }
0137     if (!child)
0138       /* no child intersects, return their father */
0139       return obj;
0140     /* found one intersecting child, look at its children */
0141     obj = child;
0142   }
0143   /* obj is included, return it */
0144   return obj;
0145 }
0146 
0147 /** \brief Get the set of largest objects covering exactly a given cpuset \p set
0148  *
0149  * \return the number of objects returned in \p objs.
0150  * \return -1 if no set of objects may cover that cpuset.
0151  */
0152 HWLOC_DECLSPEC int hwloc_get_largest_objs_inside_cpuset (hwloc_topology_t topology, hwloc_const_cpuset_t set,
0153                          hwloc_obj_t * __hwloc_restrict objs, int max);
0154 
0155 /** \brief Return the next object at depth \p depth included in CPU set \p set.
0156  *
0157  * The next invokation should pass the previous return value in \p prev
0158  * so as to obtain the next object in \p set.
0159  *
0160  * \return the first object at depth \p depth included in \p set if \p prev is \c NULL.
0161  * \return the next object at depth \p depth included in \p set if \p prev is not \c NULL.
0162  * \return \c NULL if there is no next object.
0163  *
0164  * \note Objects with empty CPU sets are ignored
0165  * (otherwise they would be considered included in any given set).
0166  *
0167  * \note This function cannot work if objects at the given depth do
0168  * not have CPU sets (I/O or Misc objects).
0169  */
0170 static __hwloc_inline hwloc_obj_t
0171 hwloc_get_next_obj_inside_cpuset_by_depth (hwloc_topology_t topology, hwloc_const_cpuset_t set,
0172                        int depth, hwloc_obj_t prev)
0173 {
0174   hwloc_obj_t next = hwloc_get_next_obj_by_depth(topology, depth, prev);
0175   if (!next)
0176     return NULL;
0177   while (next && (hwloc_bitmap_iszero(next->cpuset) || !hwloc_bitmap_isincluded(next->cpuset, set)))
0178     next = next->next_cousin;
0179   return next;
0180 }
0181 
0182 /** \brief Return the next object of type \p type included in CPU set \p set.
0183  *
0184  * The next invokation should pass the previous return value in \p prev
0185  * so as to obtain the next object in \p set.
0186  *
0187  * \return the first object of type \p type included in \p set if \p prev is \c NULL.
0188  * \return the next object of type \p type included in \p set if \p prev is not \c NULL.
0189  * \return \c NULL if there is no next object.
0190  * \return \c NULL if there is no depth for the given type.
0191  * \return \c NULL if there are multiple depths for the given type,
0192  * the caller should fallback to hwloc_get_next_obj_inside_cpuset_by_depth().
0193  *
0194  * \note Objects with empty CPU sets are ignored
0195  * (otherwise they would be considered included in any given set).
0196  *
0197  * \note This function cannot work if objects of the given type do
0198  * not have CPU sets (I/O or Misc objects).
0199  */
0200 static __hwloc_inline hwloc_obj_t
0201 hwloc_get_next_obj_inside_cpuset_by_type (hwloc_topology_t topology, hwloc_const_cpuset_t set,
0202                       hwloc_obj_type_t type, hwloc_obj_t prev)
0203 {
0204   int depth = hwloc_get_type_depth(topology, type);
0205   if (depth == HWLOC_TYPE_DEPTH_UNKNOWN || depth == HWLOC_TYPE_DEPTH_MULTIPLE)
0206     return NULL;
0207   return hwloc_get_next_obj_inside_cpuset_by_depth(topology, set, depth, prev);
0208 }
0209 
0210 /** \brief Return the (logically) \p idx -th object at depth \p depth included in CPU set \p set.
0211  *
0212  * \return the object if any, \c NULL otherwise.
0213  *
0214  * \note Objects with empty CPU sets are ignored
0215  * (otherwise they would be considered included in any given set).
0216  *
0217  * \note This function cannot work if objects at the given depth do
0218  * not have CPU sets (I/O or Misc objects).
0219  */
0220 static __hwloc_inline hwloc_obj_t
0221 hwloc_get_obj_inside_cpuset_by_depth (hwloc_topology_t topology, hwloc_const_cpuset_t set,
0222                       int depth, unsigned idx) __hwloc_attribute_pure;
0223 static __hwloc_inline hwloc_obj_t
0224 hwloc_get_obj_inside_cpuset_by_depth (hwloc_topology_t topology, hwloc_const_cpuset_t set,
0225                       int depth, unsigned idx)
0226 {
0227   hwloc_obj_t obj = hwloc_get_obj_by_depth (topology, depth, 0);
0228   unsigned count = 0;
0229   if (!obj)
0230     return NULL;
0231   while (obj) {
0232     if (!hwloc_bitmap_iszero(obj->cpuset) && hwloc_bitmap_isincluded(obj->cpuset, set)) {
0233       if (count == idx)
0234     return obj;
0235       count++;
0236     }
0237     obj = obj->next_cousin;
0238   }
0239   return NULL;
0240 }
0241 
0242 /** \brief Return the \p idx -th object of type \p type included in CPU set \p set.
0243  *
0244  * \return the object if any.
0245  * \return \c NULL if there is no such object.
0246  * \return \c NULL if there is no depth for given type.
0247  * \return \c NULL if there are multiple depths for given type,
0248  * the caller should fallback to hwloc_get_obj_inside_cpuset_by_depth().
0249  *
0250  * \note Objects with empty CPU sets are ignored
0251  * (otherwise they would be considered included in any given set).
0252  *
0253  * \note This function cannot work if objects of the given type do
0254  * not have CPU sets (I/O or Misc objects).
0255  */
0256 static __hwloc_inline hwloc_obj_t
0257 hwloc_get_obj_inside_cpuset_by_type (hwloc_topology_t topology, hwloc_const_cpuset_t set,
0258                      hwloc_obj_type_t type, unsigned idx) __hwloc_attribute_pure;
0259 static __hwloc_inline hwloc_obj_t
0260 hwloc_get_obj_inside_cpuset_by_type (hwloc_topology_t topology, hwloc_const_cpuset_t set,
0261                      hwloc_obj_type_t type, unsigned idx)
0262 {
0263   int depth = hwloc_get_type_depth(topology, type);
0264   if (depth == HWLOC_TYPE_DEPTH_UNKNOWN || depth == HWLOC_TYPE_DEPTH_MULTIPLE)
0265     return NULL;
0266   return hwloc_get_obj_inside_cpuset_by_depth(topology, set, depth, idx);
0267 }
0268 
0269 /** \brief Return the number of objects at depth \p depth included in CPU set \p set.
0270  *
0271  * \return the number of objects.
0272  * \return 0 if the depth is invalid.
0273  *
0274  * \note Objects with empty CPU sets are ignored
0275  * (otherwise they would be considered included in any given set).
0276  *
0277  * \note This function cannot work if objects at the given depth do
0278  * not have CPU sets (I/O or Misc objects).
0279  */
0280 static __hwloc_inline unsigned
0281 hwloc_get_nbobjs_inside_cpuset_by_depth (hwloc_topology_t topology, hwloc_const_cpuset_t set,
0282                      int depth) __hwloc_attribute_pure;
0283 static __hwloc_inline unsigned
0284 hwloc_get_nbobjs_inside_cpuset_by_depth (hwloc_topology_t topology, hwloc_const_cpuset_t set,
0285                      int depth)
0286 {
0287   hwloc_obj_t obj = hwloc_get_obj_by_depth (topology, depth, 0);
0288   unsigned count = 0;
0289   if (!obj)
0290     return 0;
0291   while (obj) {
0292     if (!hwloc_bitmap_iszero(obj->cpuset) && hwloc_bitmap_isincluded(obj->cpuset, set))
0293       count++;
0294     obj = obj->next_cousin;
0295   }
0296   return count;
0297 }
0298 
0299 /** \brief Return the number of objects of type \p type included in CPU set \p set.
0300  *
0301  * \return the number of objects.
0302  * \return 0 if there are no objects of that type in the topology.
0303  * \return -1 if there are multiple levels of objects of that type,
0304  * the caller should fallback to hwloc_get_nbobjs_inside_cpuset_by_depth().
0305  *
0306  * \note Objects with empty CPU sets are ignored
0307  * (otherwise they would be considered included in any given set).
0308  *
0309  * \note This function cannot work if objects of the given type do
0310  * not have CPU sets (I/O objects).
0311  */
0312 static __hwloc_inline int
0313 hwloc_get_nbobjs_inside_cpuset_by_type (hwloc_topology_t topology, hwloc_const_cpuset_t set,
0314                     hwloc_obj_type_t type) __hwloc_attribute_pure;
0315 static __hwloc_inline int
0316 hwloc_get_nbobjs_inside_cpuset_by_type (hwloc_topology_t topology, hwloc_const_cpuset_t set,
0317                     hwloc_obj_type_t type)
0318 {
0319   int depth = hwloc_get_type_depth(topology, type);
0320   if (depth == HWLOC_TYPE_DEPTH_UNKNOWN)
0321     return 0;
0322   if (depth == HWLOC_TYPE_DEPTH_MULTIPLE)
0323     return -1; /* FIXME: agregate nbobjs from different levels? */
0324   return (int) hwloc_get_nbobjs_inside_cpuset_by_depth(topology, set, depth);
0325 }
0326 
0327 /** \brief Return the logical index among the objects included in CPU set \p set.
0328  *
0329  * Consult all objects in the same level as \p obj and inside CPU set \p set
0330  * in the logical order, and return the index of \p obj within them.
0331  * If \p set covers the entire topology, this is the logical index of \p obj.
0332  * Otherwise, this is similar to a logical index within the part of the topology
0333  * defined by CPU set \p set.
0334  *
0335  * \return the logical index among the objects included in the set if any.
0336  * \return -1 if the object is not included in the set.
0337  *
0338  * \note Objects with empty CPU sets are ignored
0339  * (otherwise they would be considered included in any given set).
0340  *
0341  * \note This function cannot work if obj does not have CPU sets (I/O objects).
0342  */
0343 static __hwloc_inline int
0344 hwloc_get_obj_index_inside_cpuset (hwloc_topology_t topology __hwloc_attribute_unused, hwloc_const_cpuset_t set,
0345                    hwloc_obj_t obj) __hwloc_attribute_pure;
0346 static __hwloc_inline int
0347 hwloc_get_obj_index_inside_cpuset (hwloc_topology_t topology __hwloc_attribute_unused, hwloc_const_cpuset_t set,
0348                    hwloc_obj_t obj)
0349 {
0350   int idx = 0;
0351   if (!hwloc_bitmap_isincluded(obj->cpuset, set))
0352     return -1;
0353   /* count how many objects are inside the cpuset on the way from us to the beginning of the level */
0354   while ((obj = obj->prev_cousin) != NULL)
0355     if (!hwloc_bitmap_iszero(obj->cpuset) && hwloc_bitmap_isincluded(obj->cpuset, set))
0356       idx++;
0357   return idx;
0358 }
0359 
0360 /** @} */
0361 
0362 
0363 
0364 /** \defgroup hwlocality_helper_find_covering Finding Objects covering at least CPU set
0365  * @{
0366  */
0367 
0368 /** \brief Get the child covering at least CPU set \p set.
0369  *
0370  * \return the child that covers the set entirely.
0371  * \return \c NULL if no child matches or if \p set is empty.
0372  *
0373  * \note This function cannot work if parent does not have a CPU set (I/O or Misc objects).
0374  */
0375 static __hwloc_inline hwloc_obj_t
0376 hwloc_get_child_covering_cpuset (hwloc_topology_t topology __hwloc_attribute_unused, hwloc_const_cpuset_t set,
0377                 hwloc_obj_t parent) __hwloc_attribute_pure;
0378 static __hwloc_inline hwloc_obj_t
0379 hwloc_get_child_covering_cpuset (hwloc_topology_t topology __hwloc_attribute_unused, hwloc_const_cpuset_t set,
0380                 hwloc_obj_t parent)
0381 {
0382   hwloc_obj_t child;
0383   if (hwloc_bitmap_iszero(set))
0384     return NULL;
0385   child = parent->first_child;
0386   while (child) {
0387     if (child->cpuset && hwloc_bitmap_isincluded(set, child->cpuset))
0388       return child;
0389     child = child->next_sibling;
0390   }
0391   return NULL;
0392 }
0393 
0394 /** \brief Get the lowest object covering at least CPU set \p set
0395  *
0396  * \return the lowest object covering the set entirely.
0397  * \return \c NULL if no object matches or if \p set is empty.
0398  */
0399 static __hwloc_inline hwloc_obj_t
0400 hwloc_get_obj_covering_cpuset (hwloc_topology_t topology, hwloc_const_cpuset_t set) __hwloc_attribute_pure;
0401 static __hwloc_inline hwloc_obj_t
0402 hwloc_get_obj_covering_cpuset (hwloc_topology_t topology, hwloc_const_cpuset_t set)
0403 {
0404   struct hwloc_obj *current = hwloc_get_root_obj(topology);
0405   if (hwloc_bitmap_iszero(set) || !hwloc_bitmap_isincluded(set, current->cpuset))
0406     return NULL;
0407   while (1) {
0408     hwloc_obj_t child = hwloc_get_child_covering_cpuset(topology, set, current);
0409     if (!child)
0410       return current;
0411     current = child;
0412   }
0413 }
0414 
0415 /** \brief Iterate through same-depth objects covering at least CPU set \p set
0416  *
0417  * The next invokation should pass the previous return value in \p prev so as
0418  * to obtain the next object covering at least another part of \p set.
0419  *
0420  * \return the first object at depth \p depth covering at least part of CPU set \p set
0421  * if object \p prev is \c NULL.
0422  * \return the next one if \p prev is not \c NULL.
0423  * \return \c NULL if there is no next object.
0424  *
0425  * \note This function cannot work if objects at the given depth do
0426  * not have CPU sets (I/O or Misc objects).
0427  */
0428 static __hwloc_inline hwloc_obj_t
0429 hwloc_get_next_obj_covering_cpuset_by_depth(hwloc_topology_t topology, hwloc_const_cpuset_t set,
0430                         int depth, hwloc_obj_t prev)
0431 {
0432   hwloc_obj_t next = hwloc_get_next_obj_by_depth(topology, depth, prev);
0433   if (!next)
0434     return NULL;
0435   while (next && !hwloc_bitmap_intersects(set, next->cpuset))
0436     next = next->next_cousin;
0437   return next;
0438 }
0439 
0440 /** \brief Iterate through same-type objects covering at least CPU set \p set
0441  *
0442  * The next invokation should pass the previous return value in \p prev so as to obtain
0443  * the next object of type \p type covering at least another part of \p set.
0444  *
0445  * \return the first object of type \p type covering at least part of CPU set \p set
0446  * if object \p prev is \c NULL.
0447  * \return the next one if \p prev is not \c NULL.
0448  * \return \c NULL if there is no next object.
0449  * \return \c NULL if there is no depth for the given type.
0450  * \return \c NULL if there are multiple depths for the given type,
0451  * the caller should fallback to hwloc_get_next_obj_covering_cpuset_by_depth().
0452  *
0453  * \note This function cannot work if objects of the given type do
0454  * not have CPU sets (I/O or Misc objects).
0455  */
0456 static __hwloc_inline hwloc_obj_t
0457 hwloc_get_next_obj_covering_cpuset_by_type(hwloc_topology_t topology, hwloc_const_cpuset_t set,
0458                        hwloc_obj_type_t type, hwloc_obj_t prev)
0459 {
0460   int depth = hwloc_get_type_depth(topology, type);
0461   if (depth == HWLOC_TYPE_DEPTH_UNKNOWN || depth == HWLOC_TYPE_DEPTH_MULTIPLE)
0462     return NULL;
0463   return hwloc_get_next_obj_covering_cpuset_by_depth(topology, set, depth, prev);
0464 }
0465 
0466 /** @} */
0467 
0468 
0469 
0470 /** \defgroup hwlocality_helper_ancestors Looking at Ancestor and Child Objects
0471  * @{
0472  *
0473  * Be sure to see the figure in \ref termsanddefs that shows a
0474  * complete topology tree, including depths, child/sibling/cousin
0475  * relationships, and an example of an asymmetric topology where one
0476  * package has fewer caches than its peers.
0477  */
0478 
0479 /** \brief Returns the ancestor object of \p obj at depth \p depth.
0480  *
0481  * \return the ancestor if any.
0482  * \return \c NULL if no such ancestor exists.
0483  *
0484  * \note \p depth should not be the depth of PU or NUMA objects
0485  * since they are ancestors of no objects (except Misc or I/O).
0486  * This function rather expects an intermediate level depth,
0487  * such as the depth of Packages, Cores, or Caches.
0488  */
0489 static __hwloc_inline hwloc_obj_t
0490 hwloc_get_ancestor_obj_by_depth (hwloc_topology_t topology __hwloc_attribute_unused, int depth, hwloc_obj_t obj) __hwloc_attribute_pure;
0491 static __hwloc_inline hwloc_obj_t
0492 hwloc_get_ancestor_obj_by_depth (hwloc_topology_t topology __hwloc_attribute_unused, int depth, hwloc_obj_t obj)
0493 {
0494   hwloc_obj_t ancestor = obj;
0495   if (obj->depth < depth)
0496     return NULL;
0497   while (ancestor && ancestor->depth > depth)
0498     ancestor = ancestor->parent;
0499   return ancestor;
0500 }
0501 
0502 /** \brief Returns the ancestor object of \p obj with type \p type.
0503  *
0504  * \return the ancestor if any.
0505  * \return \c NULL if no such ancestor exists.
0506  *
0507  * \note if multiple matching ancestors exist (e.g. multiple levels of ::HWLOC_OBJ_GROUP)
0508  * the lowest one is returned.
0509  *
0510  * \note \p type should not be ::HWLOC_OBJ_PU or ::HWLOC_OBJ_NUMANODE
0511  * since these objects are ancestors of no objects (except Misc or I/O).
0512  * This function rather expects an intermediate object type,
0513  * such as ::HWLOC_OBJ_PACKAGE, ::HWLOC_OBJ_CORE, etc.
0514  */
0515 static __hwloc_inline hwloc_obj_t
0516 hwloc_get_ancestor_obj_by_type (hwloc_topology_t topology __hwloc_attribute_unused, hwloc_obj_type_t type, hwloc_obj_t obj) __hwloc_attribute_pure;
0517 static __hwloc_inline hwloc_obj_t
0518 hwloc_get_ancestor_obj_by_type (hwloc_topology_t topology __hwloc_attribute_unused, hwloc_obj_type_t type, hwloc_obj_t obj)
0519 {
0520   hwloc_obj_t ancestor = obj->parent;
0521   while (ancestor && ancestor->type != type)
0522     ancestor = ancestor->parent;
0523   return ancestor;
0524 }
0525 
0526 /** \brief Returns the common parent object to objects \p obj1 and \p obj2.
0527  *
0528  * \return the common ancestor.
0529  *
0530  * \note This function cannot return \c NULL.
0531  */
0532 static __hwloc_inline hwloc_obj_t
0533 hwloc_get_common_ancestor_obj (hwloc_topology_t topology __hwloc_attribute_unused, hwloc_obj_t obj1, hwloc_obj_t obj2) __hwloc_attribute_pure;
0534 static __hwloc_inline hwloc_obj_t
0535 hwloc_get_common_ancestor_obj (hwloc_topology_t topology __hwloc_attribute_unused, hwloc_obj_t obj1, hwloc_obj_t obj2)
0536 {
0537   /* the loop isn't so easy since intermediate ancestors may have
0538    * different depth, causing us to alternate between using obj1->parent
0539    * and obj2->parent. Also, even if at some point we find ancestors of
0540    * of the same depth, their ancestors may have different depth again.
0541    */
0542   while (obj1 != obj2) {
0543     while (obj1->depth > obj2->depth)
0544       obj1 = obj1->parent;
0545     while (obj2->depth > obj1->depth)
0546       obj2 = obj2->parent;
0547     if (obj1 != obj2 && obj1->depth == obj2->depth) {
0548       obj1 = obj1->parent;
0549       obj2 = obj2->parent;
0550     }
0551   }
0552   return obj1;
0553 }
0554 
0555 /** \brief Returns true if \p obj is inside the subtree beginning with ancestor object \p subtree_root.
0556  *
0557  * \return 1 is the object is in the subtree, 0 otherwise.
0558  *
0559  * \note This function cannot work if \p obj and \p subtree_root objects do
0560  * not have CPU sets (I/O or Misc objects).
0561  */
0562 static __hwloc_inline int
0563 hwloc_obj_is_in_subtree (hwloc_topology_t topology __hwloc_attribute_unused, hwloc_obj_t obj, hwloc_obj_t subtree_root) __hwloc_attribute_pure;
0564 static __hwloc_inline int
0565 hwloc_obj_is_in_subtree (hwloc_topology_t topology __hwloc_attribute_unused, hwloc_obj_t obj, hwloc_obj_t subtree_root)
0566 {
0567   return obj->cpuset && subtree_root->cpuset && hwloc_bitmap_isincluded(obj->cpuset, subtree_root->cpuset);
0568 }
0569 
0570 /** \brief Return the next child.
0571  *
0572  * Return the next child among the normal children list,
0573  * then among the memory children list, then among the I/O
0574  * children list, then among the Misc children list.
0575  *
0576  * \return the first child if \p prev is \c NULL.
0577  * \return the next child if \p prev is not \c NULL.
0578  * \return \c NULL when there is no next child.
0579  */
0580 static __hwloc_inline hwloc_obj_t
0581 hwloc_get_next_child (hwloc_topology_t topology __hwloc_attribute_unused, hwloc_obj_t parent, hwloc_obj_t prev)
0582 {
0583   hwloc_obj_t obj;
0584   int state = 0;
0585   if (prev) {
0586     if (prev->type == HWLOC_OBJ_MISC)
0587       state = 3;
0588     else if (hwloc_obj_type_is_io(prev->type))
0589       state = 2;
0590     else if (hwloc_obj_type_is_memory(prev->type))
0591       state = 1;
0592     obj = prev->next_sibling;
0593   } else {
0594     obj = parent->first_child;
0595   }
0596   if (!obj && state == 0) {
0597     obj = parent->memory_first_child;
0598     state = 1;
0599   }
0600   if (!obj && state == 1) {
0601     obj = parent->io_first_child;
0602     state = 2;
0603   }
0604   if (!obj && state == 2) {
0605     obj = parent->misc_first_child;
0606     state = 3;
0607   }
0608   return obj;
0609 }
0610 
0611 /** @} */
0612 
0613 
0614 
0615 /** \defgroup hwlocality_helper_find_cache Looking at Cache Objects
0616  * @{
0617  */
0618 
0619 /** \brief Find the depth of cache objects matching cache level and type.
0620  *
0621  * Return the depth of the topology level that contains cache objects
0622  * whose attributes match \p cachelevel and \p cachetype.
0623 
0624  * This function is identical to calling hwloc_get_type_depth() with the
0625  * corresponding type such as ::HWLOC_OBJ_L1ICACHE, except that it may
0626  * also return a Unified cache when looking for an instruction cache.
0627  *
0628  * \return the depth of the unique matching unified cache level is returned
0629  * if \p cachetype is ::HWLOC_OBJ_CACHE_UNIFIED.
0630  *
0631  * \return the depth of either a matching cache level or a unified cache level
0632  * if \p cachetype is ::HWLOC_OBJ_CACHE_DATA or ::HWLOC_OBJ_CACHE_INSTRUCTION.
0633  *
0634  * \return the depth of the matching level
0635  * if \p cachetype is \c -1 but only one level matches.
0636  *
0637  * \return ::HWLOC_TYPE_DEPTH_MULTIPLE
0638  * if \p cachetype is \c -1 but multiple levels match.
0639  *
0640  * \return ::HWLOC_TYPE_DEPTH_UNKNOWN if no cache level matches.
0641  */
0642 static __hwloc_inline int
0643 hwloc_get_cache_type_depth (hwloc_topology_t topology,
0644                 unsigned cachelevel, hwloc_obj_cache_type_t cachetype)
0645 {
0646   int depth;
0647   int found = HWLOC_TYPE_DEPTH_UNKNOWN;
0648   for (depth=0; ; depth++) {
0649     hwloc_obj_t obj = hwloc_get_obj_by_depth(topology, depth, 0);
0650     if (!obj)
0651       break;
0652     if (!hwloc_obj_type_is_dcache(obj->type) || obj->attr->cache.depth != cachelevel)
0653       /* doesn't match, try next depth */
0654       continue;
0655     if (cachetype == (hwloc_obj_cache_type_t) -1) {
0656       if (found != HWLOC_TYPE_DEPTH_UNKNOWN) {
0657     /* second match, return MULTIPLE */
0658         return HWLOC_TYPE_DEPTH_MULTIPLE;
0659       }
0660       /* first match, mark it as found */
0661       found = depth;
0662       continue;
0663     }
0664     if (obj->attr->cache.type == cachetype || obj->attr->cache.type == HWLOC_OBJ_CACHE_UNIFIED)
0665       /* exact match (either unified is alone, or we match instruction or data), return immediately */
0666       return depth;
0667   }
0668   /* went to the bottom, return what we found */
0669   return found;
0670 }
0671 
0672 /** \brief Get the first data (or unified) cache covering a cpuset \p set
0673  *
0674  * \return a covering cache, or \c NULL if no cache matches.
0675  */
0676 static __hwloc_inline hwloc_obj_t
0677 hwloc_get_cache_covering_cpuset (hwloc_topology_t topology, hwloc_const_cpuset_t set) __hwloc_attribute_pure;
0678 static __hwloc_inline hwloc_obj_t
0679 hwloc_get_cache_covering_cpuset (hwloc_topology_t topology, hwloc_const_cpuset_t set)
0680 {
0681   hwloc_obj_t current = hwloc_get_obj_covering_cpuset(topology, set);
0682   while (current) {
0683     if (hwloc_obj_type_is_dcache(current->type))
0684       return current;
0685     current = current->parent;
0686   }
0687   return NULL;
0688 }
0689 
0690 /** \brief Get the first data (or unified) cache shared between an object and somebody else.
0691  *
0692  * \return a shared cache.
0693  * \return \c NULL if no cache matches or if an invalid object is given (e.g. I/O object).
0694  */
0695 static __hwloc_inline hwloc_obj_t
0696 hwloc_get_shared_cache_covering_obj (hwloc_topology_t topology __hwloc_attribute_unused, hwloc_obj_t obj) __hwloc_attribute_pure;
0697 static __hwloc_inline hwloc_obj_t
0698 hwloc_get_shared_cache_covering_obj (hwloc_topology_t topology __hwloc_attribute_unused, hwloc_obj_t obj)
0699 {
0700   hwloc_obj_t current = obj->parent;
0701   if (!obj->cpuset)
0702     return NULL;
0703   while (current) {
0704     if (!hwloc_bitmap_isequal(current->cpuset, obj->cpuset)
0705         && hwloc_obj_type_is_dcache(current->type))
0706       return current;
0707     current = current->parent;
0708   }
0709   return NULL;
0710 }
0711 
0712 /** @} */
0713 
0714 
0715 
0716 /** \defgroup hwlocality_helper_find_misc Finding objects, miscellaneous helpers
0717  * @{
0718  *
0719  * Be sure to see the figure in \ref termsanddefs that shows a
0720  * complete topology tree, including depths, child/sibling/cousin
0721  * relationships, and an example of an asymmetric topology where one
0722  * package has fewer caches than its peers.
0723  */
0724 
0725 /** \brief Remove simultaneous multithreading PUs from a CPU set.
0726  *
0727  * For each core in \p topology, if \p cpuset contains some PUs of that core,
0728  * modify \p cpuset to only keep a single PU for that core.
0729  *
0730  * \p which specifies which PU will be kept.
0731  * PU are considered in physical index order.
0732  * If 0, for each core, the function keeps the first PU that was originally set in \p cpuset.
0733  *
0734  * If \p which is larger than the number of PUs in a core there were originally set in \p cpuset,
0735  * no PU is kept for that core.
0736  *
0737  * \return 0.
0738  *
0739  * \note PUs that are not below a Core object are ignored
0740  * (for instance if the topology does not contain any Core object).
0741  * None of them is removed from \p cpuset.
0742  */
0743 HWLOC_DECLSPEC int hwloc_bitmap_singlify_per_core(hwloc_topology_t topology, hwloc_bitmap_t cpuset, unsigned which);
0744 
0745 /** \brief Returns the object of type ::HWLOC_OBJ_PU with \p os_index.
0746  *
0747  * This function is useful for converting a CPU set into the PU
0748  * objects it contains.
0749  * When retrieving the current binding (e.g. with hwloc_get_cpubind()),
0750  * one may iterate over the bits of the resulting CPU set with
0751  * hwloc_bitmap_foreach_begin(), and find the corresponding PUs
0752  * with this function.
0753  *
0754  * \return the PU object, or \c NULL if none matches.
0755  */
0756 static __hwloc_inline hwloc_obj_t
0757 hwloc_get_pu_obj_by_os_index(hwloc_topology_t topology, unsigned os_index) __hwloc_attribute_pure;
0758 static __hwloc_inline hwloc_obj_t
0759 hwloc_get_pu_obj_by_os_index(hwloc_topology_t topology, unsigned os_index)
0760 {
0761   hwloc_obj_t obj = NULL;
0762   while ((obj = hwloc_get_next_obj_by_type(topology, HWLOC_OBJ_PU, obj)) != NULL)
0763     if (obj->os_index == os_index)
0764       return obj;
0765   return NULL;
0766 }
0767 
0768 /** \brief Returns the object of type ::HWLOC_OBJ_NUMANODE with \p os_index.
0769  *
0770  * This function is useful for converting a nodeset into the NUMA node
0771  * objects it contains.
0772  * When retrieving the current binding (e.g. with hwloc_get_membind() with HWLOC_MEMBIND_BYNODESET),
0773  * one may iterate over the bits of the resulting nodeset with
0774  * hwloc_bitmap_foreach_begin(), and find the corresponding NUMA nodes
0775  * with this function.
0776  *
0777  * \return the NUMA node object, or \c NULL if none matches.
0778  */
0779 static __hwloc_inline hwloc_obj_t
0780 hwloc_get_numanode_obj_by_os_index(hwloc_topology_t topology, unsigned os_index) __hwloc_attribute_pure;
0781 static __hwloc_inline hwloc_obj_t
0782 hwloc_get_numanode_obj_by_os_index(hwloc_topology_t topology, unsigned os_index)
0783 {
0784   hwloc_obj_t obj = NULL;
0785   while ((obj = hwloc_get_next_obj_by_type(topology, HWLOC_OBJ_NUMANODE, obj)) != NULL)
0786     if (obj->os_index == os_index)
0787       return obj;
0788   return NULL;
0789 }
0790 
0791 /** \brief Do a depth-first traversal of the topology to find and sort
0792  *
0793  * all objects that are at the same depth than \p src.
0794  * Report in \p objs up to \p max physically closest ones to \p src.
0795  *
0796  * \return the number of objects returned in \p objs.
0797  *
0798  * \return 0 if \p src is an I/O object.
0799  *
0800  * \note This function requires the \p src object to have a CPU set.
0801  */
0802 /* TODO: rather provide an iterator? Provide a way to know how much should be allocated? By returning the total number of objects instead? */
0803 HWLOC_DECLSPEC unsigned hwloc_get_closest_objs (hwloc_topology_t topology, hwloc_obj_t src, hwloc_obj_t * __hwloc_restrict objs, unsigned max);
0804 
0805 /** \brief Find an object below another object, both specified by types and indexes.
0806  *
0807  * Start from the top system object and find object of type \p type1
0808  * and logical index \p idx1.  Then look below this object and find another
0809  * object of type \p type2 and logical index \p idx2.  Indexes are specified
0810  * within the parent, not withing the entire system.
0811  *
0812  * For instance, if type1 is PACKAGE, idx1 is 2, type2 is CORE and idx2
0813  * is 3, return the fourth core object below the third package.
0814  *
0815  * \return a matching object if any, \c NULL otherwise.
0816  *
0817  * \note This function requires these objects to have a CPU set.
0818  */
0819 static __hwloc_inline hwloc_obj_t
0820 hwloc_get_obj_below_by_type (hwloc_topology_t topology,
0821                  hwloc_obj_type_t type1, unsigned idx1,
0822                  hwloc_obj_type_t type2, unsigned idx2) __hwloc_attribute_pure;
0823 static __hwloc_inline hwloc_obj_t
0824 hwloc_get_obj_below_by_type (hwloc_topology_t topology,
0825                  hwloc_obj_type_t type1, unsigned idx1,
0826                  hwloc_obj_type_t type2, unsigned idx2)
0827 {
0828   hwloc_obj_t obj;
0829   obj = hwloc_get_obj_by_type (topology, type1, idx1);
0830   if (!obj)
0831     return NULL;
0832   return hwloc_get_obj_inside_cpuset_by_type(topology, obj->cpuset, type2, idx2);
0833 }
0834 
0835 /** \brief Find an object below a chain of objects specified by types and indexes.
0836  *
0837  * This is a generalized version of hwloc_get_obj_below_by_type().
0838  *
0839  * Arrays \p typev and \p idxv must contain \p nr types and indexes.
0840  *
0841  * Start from the top system object and walk the arrays \p typev and \p idxv.
0842  * For each type and logical index couple in the arrays, look under the previously found
0843  * object to find the index-th object of the given type.
0844  * Indexes are specified within the parent, not withing the entire system.
0845  *
0846  * For instance, if nr is 3, typev contains NODE, PACKAGE and CORE,
0847  * and idxv contains 0, 1 and 2, return the third core object below
0848  * the second package below the first NUMA node.
0849  *
0850  * \return a matching object if any, \c NULL otherwise.
0851  *
0852  * \note This function requires all these objects and the root object
0853  * to have a CPU set.
0854  */
0855 static __hwloc_inline hwloc_obj_t
0856 hwloc_get_obj_below_array_by_type (hwloc_topology_t topology, int nr, hwloc_obj_type_t *typev, unsigned *idxv) __hwloc_attribute_pure;
0857 static __hwloc_inline hwloc_obj_t
0858 hwloc_get_obj_below_array_by_type (hwloc_topology_t topology, int nr, hwloc_obj_type_t *typev, unsigned *idxv)
0859 {
0860   hwloc_obj_t obj = hwloc_get_root_obj(topology);
0861   int i;
0862   for(i=0; i<nr; i++) {
0863     if (!obj)
0864       return NULL;
0865     obj = hwloc_get_obj_inside_cpuset_by_type(topology, obj->cpuset, typev[i], idxv[i]);
0866   }
0867   return obj;
0868 }
0869 
0870 /** \brief Return an object of a different type with same locality.
0871  *
0872  * If the source object \p src is a normal or memory type,
0873  * this function returns an object of type \p type with same
0874  * CPU and node sets, either below or above in the hierarchy.
0875  *
0876  * If the source object \p src is a PCI or an OS device within a PCI
0877  * device, the function may either return that PCI device, or another
0878  * OS device in the same PCI parent.
0879  * This may for instance be useful for converting between OS devices
0880  * such as "nvml0" or "rsmi1" used in distance structures into the
0881  * the PCI device, or the CUDA or OpenCL OS device that correspond
0882  * to the same physical card.
0883  *
0884  * If not \c NULL, parameter \p subtype only select objects whose
0885  * subtype attribute exists and is \p subtype (case-insensitively),
0886  * for instance "OpenCL" or "CUDA".
0887  *
0888  * If not \c NULL, parameter \p nameprefix only selects objects whose
0889  * name attribute exists and starts with \p nameprefix (case-insensitively),
0890  * for instance "rsmi" for matching "rsmi0".
0891  *
0892  * If multiple objects match, the first one is returned.
0893  *
0894  * This function will not walk the hierarchy across bridges since
0895  * the PCI locality may become different.
0896  * This function cannot also convert between normal/memory objects
0897  * and I/O or Misc objects.
0898  *
0899  * \p flags must be \c 0 for now.
0900  *
0901  * \return An object with identical locality,
0902  * matching \p subtype and \p nameprefix if any.
0903  *
0904  * \return \c NULL if no matching object could be found,
0905  * or if the source object and target type are incompatible,
0906  * for instance if converting between CPU and I/O objects.
0907  */
0908 HWLOC_DECLSPEC hwloc_obj_t
0909 hwloc_get_obj_with_same_locality(hwloc_topology_t topology, hwloc_obj_t src,
0910                                  hwloc_obj_type_t type, const char *subtype, const char *nameprefix,
0911                                  unsigned long flags);
0912 
0913 /** @} */
0914 
0915 
0916 
0917 /** \defgroup hwlocality_helper_distribute Distributing items over a topology
0918  * @{
0919  */
0920 
0921 /** \brief Flags to be given to hwloc_distrib().
0922  */
0923 enum hwloc_distrib_flags_e {
0924   /** \brief Distrib in reverse order, starting from the last objects.
0925    * \hideinitializer
0926    */
0927   HWLOC_DISTRIB_FLAG_REVERSE = (1UL<<0)
0928 };
0929 
0930 /** \brief Distribute \p n items over the topology under \p roots
0931  *
0932  * Array \p set will be filled with \p n cpusets recursively distributed
0933  * linearly over the topology under objects \p roots, down to depth \p until
0934  * (which can be INT_MAX to distribute down to the finest level).
0935  *
0936  * \p n_roots is usually 1 and \p roots only contains the topology root object
0937  * so as to distribute over the entire topology.
0938  *
0939  * This is typically useful when an application wants to distribute \p n
0940  * threads over a machine, giving each of them as much private cache as
0941  * possible and keeping them locally in number order.
0942  *
0943  * The caller may typically want to also call hwloc_bitmap_singlify()
0944  * before binding a thread so that it does not move at all.
0945  *
0946  * \p flags should be 0 or a OR'ed set of ::hwloc_distrib_flags_e.
0947  *
0948  * \return 0 on success, -1 on error.
0949  *
0950  * \note On hybrid CPUs (or asymmetric platforms), distribution may be suboptimal
0951  * since the number of cores or PUs inside packages or below caches may vary
0952  * (the top-down recursive partitioning ignores these numbers until reaching their levels).
0953  * Hence it is recommended to distribute only inside a single homogeneous domain.
0954  * For instance on a CPU with energy-efficient E-cores and high-performance P-cores,
0955  * one should distribute separately N tasks on E-cores and M tasks on P-cores
0956  * instead of trying to distribute directly M+N tasks on the entire CPUs.
0957  *
0958  * \note This function requires the \p roots objects to have a CPU set.
0959  */
0960 static __hwloc_inline int
0961 hwloc_distrib(hwloc_topology_t topology,
0962           hwloc_obj_t *roots, unsigned n_roots,
0963           hwloc_cpuset_t *set,
0964           unsigned n,
0965           int until, unsigned long flags)
0966 {
0967   unsigned i;
0968   unsigned tot_weight;
0969   unsigned given, givenweight;
0970   hwloc_cpuset_t *cpusetp = set;
0971 
0972   if (!n || (flags & ~HWLOC_DISTRIB_FLAG_REVERSE)) {
0973     errno = EINVAL;
0974     return -1;
0975   }
0976 
0977   tot_weight = 0;
0978   for (i = 0; i < n_roots; i++)
0979     tot_weight += (unsigned) hwloc_bitmap_weight(roots[i]->cpuset);
0980 
0981   for (i = 0, given = 0, givenweight = 0; i < n_roots; i++) {
0982     unsigned chunk, weight;
0983     hwloc_obj_t root = roots[flags & HWLOC_DISTRIB_FLAG_REVERSE ? n_roots-1-i : i];
0984     hwloc_cpuset_t cpuset = root->cpuset;
0985     while (!hwloc_obj_type_is_normal(root->type))
0986       /* If memory/io/misc, walk up to normal parent */
0987       root = root->parent;
0988     weight = (unsigned) hwloc_bitmap_weight(cpuset);
0989     if (!weight)
0990       continue;
0991     /* Give to root a chunk proportional to its weight.
0992      * If previous chunks got rounded-up, we may get a bit less. */
0993     chunk = (( (givenweight+weight) * n  + tot_weight-1) / tot_weight)
0994           - ((  givenweight         * n  + tot_weight-1) / tot_weight);
0995     if (!root->arity || chunk <= 1 || root->depth >= until) {
0996       /* We can't split any more, put everything there.  */
0997       if (chunk) {
0998     /* Fill cpusets with ours */
0999     unsigned j;
1000     for (j=0; j < chunk; j++)
1001       cpusetp[j] = hwloc_bitmap_dup(cpuset);
1002       } else {
1003     /* We got no chunk, just merge our cpuset to a previous one
1004      * (the first chunk cannot be empty)
1005      * so that this root doesn't get ignored.
1006      */
1007     assert(given);
1008     hwloc_bitmap_or(cpusetp[-1], cpusetp[-1], cpuset);
1009       }
1010     } else {
1011       /* Still more to distribute, recurse into children */
1012       hwloc_distrib(topology, root->children, root->arity, cpusetp, chunk, until, flags);
1013     }
1014     cpusetp += chunk;
1015     given += chunk;
1016     givenweight += weight;
1017   }
1018 
1019   return 0;
1020 }
1021 
1022 /** @} */
1023 
1024 
1025 
1026 /** \defgroup hwlocality_helper_topology_sets CPU and node sets of entire topologies
1027  * @{
1028  */
1029 
1030 /** \brief Get complete CPU set
1031  *
1032  * \return the complete CPU set of processors of the system.
1033  *
1034  * \note This function cannot return \c NULL.
1035  *
1036  * \note The returned cpuset is not newly allocated and should thus not be
1037  * changed or freed; hwloc_bitmap_dup() must be used to obtain a local copy.
1038  *
1039  * \note This is equivalent to retrieving the root object complete CPU-set.
1040  */
1041 HWLOC_DECLSPEC hwloc_const_cpuset_t
1042 hwloc_topology_get_complete_cpuset(hwloc_topology_t topology) __hwloc_attribute_pure;
1043 
1044 /** \brief Get topology CPU set
1045  *
1046  * \return the CPU set of processors of the system for which hwloc
1047  * provides topology information. This is equivalent to the cpuset of the
1048  * system object.
1049  *
1050  * \note This function cannot return \c NULL.
1051  *
1052  * \note The returned cpuset is not newly allocated and should thus not be
1053  * changed or freed; hwloc_bitmap_dup() must be used to obtain a local copy.
1054  *
1055  * \note This is equivalent to retrieving the root object CPU-set.
1056  */
1057 HWLOC_DECLSPEC hwloc_const_cpuset_t
1058 hwloc_topology_get_topology_cpuset(hwloc_topology_t topology) __hwloc_attribute_pure;
1059 
1060 /** \brief Get allowed CPU set
1061  *
1062  * \return the CPU set of allowed processors of the system.
1063  *
1064  * \note This function cannot return \c NULL.
1065  *
1066  * \note If the topology flag ::HWLOC_TOPOLOGY_FLAG_INCLUDE_DISALLOWED was not set,
1067  * this is identical to hwloc_topology_get_topology_cpuset(), which means
1068  * all PUs are allowed.
1069  *
1070  * \note If ::HWLOC_TOPOLOGY_FLAG_INCLUDE_DISALLOWED was set, applying
1071  * hwloc_bitmap_intersects() on the result of this function and on an object
1072  * cpuset checks whether there are allowed PUs inside that object.
1073  * Applying hwloc_bitmap_and() returns the list of these allowed PUs.
1074  *
1075  * \note The returned cpuset is not newly allocated and should thus not be
1076  * changed or freed, hwloc_bitmap_dup() must be used to obtain a local copy.
1077  */
1078 HWLOC_DECLSPEC hwloc_const_cpuset_t
1079 hwloc_topology_get_allowed_cpuset(hwloc_topology_t topology) __hwloc_attribute_pure;
1080 
1081 /** \brief Get complete node set
1082  *
1083  * \return the complete node set of memory of the system.
1084  *
1085  * \note This function cannot return \c NULL.
1086  *
1087  * \note The returned nodeset is not newly allocated and should thus not be
1088  * changed or freed; hwloc_bitmap_dup() must be used to obtain a local copy.
1089  *
1090  * \note This is equivalent to retrieving the root object complete nodeset.
1091  */
1092 HWLOC_DECLSPEC hwloc_const_nodeset_t
1093 hwloc_topology_get_complete_nodeset(hwloc_topology_t topology) __hwloc_attribute_pure;
1094 
1095 /** \brief Get topology node set
1096  *
1097  * \return the node set of memory of the system for which hwloc
1098  * provides topology information. This is equivalent to the nodeset of the
1099  * system object.
1100  *
1101  * \note This function cannot return \c NULL.
1102  *
1103  * \note The returned nodeset is not newly allocated and should thus not be
1104  * changed or freed; hwloc_bitmap_dup() must be used to obtain a local copy.
1105  *
1106  * \note This is equivalent to retrieving the root object nodeset.
1107  */
1108 HWLOC_DECLSPEC hwloc_const_nodeset_t
1109 hwloc_topology_get_topology_nodeset(hwloc_topology_t topology) __hwloc_attribute_pure;
1110 
1111 /** \brief Get allowed node set
1112  *
1113  * \return the node set of allowed memory of the system.
1114  *
1115  * \note This function cannot return \c NULL.
1116  *
1117  * \note If the topology flag ::HWLOC_TOPOLOGY_FLAG_INCLUDE_DISALLOWED was not set,
1118  * this is identical to hwloc_topology_get_topology_nodeset(), which means
1119  * all NUMA nodes are allowed.
1120  *
1121  * \note If ::HWLOC_TOPOLOGY_FLAG_INCLUDE_DISALLOWED was set, applying
1122  * hwloc_bitmap_intersects() on the result of this function and on an object
1123  * nodeset checks whether there are allowed NUMA nodes inside that object.
1124  * Applying hwloc_bitmap_and() returns the list of these allowed NUMA nodes.
1125  *
1126  * \note The returned nodeset is not newly allocated and should thus not be
1127  * changed or freed, hwloc_bitmap_dup() must be used to obtain a local copy.
1128  */
1129 HWLOC_DECLSPEC hwloc_const_nodeset_t
1130 hwloc_topology_get_allowed_nodeset(hwloc_topology_t topology) __hwloc_attribute_pure;
1131 
1132 /** @} */
1133 
1134 
1135 
1136 /** \defgroup hwlocality_helper_nodeset_convert Converting between CPU sets and node sets
1137  *
1138  * @{
1139  */
1140 
1141 /** \brief Convert a CPU set into a NUMA node set
1142  *
1143  * For each PU included in the input \p _cpuset, set the corresponding
1144  * local NUMA node(s) in the output \p nodeset.
1145  *
1146  * If some NUMA nodes have no CPUs at all, this function never sets their
1147  * indexes in the output node set, even if a full CPU set is given in input.
1148  *
1149  * Hence the entire topology CPU set is converted into the set of all nodes
1150  * that have some local CPUs.
1151  *
1152  * \return 0 on success.
1153  * \return -1 with errno set to \c ENOMEM on internal reallocation failure.
1154  */
1155 static __hwloc_inline int
1156 hwloc_cpuset_to_nodeset(hwloc_topology_t topology, hwloc_const_cpuset_t _cpuset, hwloc_nodeset_t nodeset)
1157 {
1158     int depth = hwloc_get_type_depth(topology, HWLOC_OBJ_NUMANODE);
1159     hwloc_obj_t obj = NULL;
1160     assert(depth != HWLOC_TYPE_DEPTH_UNKNOWN);
1161     hwloc_bitmap_zero(nodeset);
1162     while ((obj = hwloc_get_next_obj_covering_cpuset_by_depth(topology, _cpuset, depth, obj)) != NULL)
1163         if (hwloc_bitmap_set(nodeset, obj->os_index) < 0)
1164             return -1;
1165     return 0;
1166 }
1167 
1168 /** \brief Convert a NUMA node set into a CPU set
1169  *
1170  * For each NUMA node included in the input \p nodeset, set the corresponding
1171  * local PUs in the output \p _cpuset.
1172  *
1173  * If some CPUs have no local NUMA nodes, this function never sets their
1174  * indexes in the output CPU set, even if a full node set is given in input.
1175  *
1176  * Hence the entire topology node set is converted into the set of all CPUs
1177  * that have some local NUMA nodes.
1178  *
1179  * \return 0 on success.
1180  * \return -1 with errno set to \c ENOMEM on internal reallocation failure.
1181  */
1182 static __hwloc_inline int
1183 hwloc_cpuset_from_nodeset(hwloc_topology_t topology, hwloc_cpuset_t _cpuset, hwloc_const_nodeset_t nodeset)
1184 {
1185     int depth = hwloc_get_type_depth(topology, HWLOC_OBJ_NUMANODE);
1186     hwloc_obj_t obj = NULL;
1187     assert(depth != HWLOC_TYPE_DEPTH_UNKNOWN);
1188     hwloc_bitmap_zero(_cpuset);
1189     while ((obj = hwloc_get_next_obj_by_depth(topology, depth, obj)) != NULL) {
1190         if (hwloc_bitmap_isset(nodeset, obj->os_index))
1191             /* no need to check obj->cpuset because objects in levels always have a cpuset */
1192             if (hwloc_bitmap_or(_cpuset, _cpuset, obj->cpuset) < 0)
1193                 return -1;
1194     }
1195     return 0;
1196 }
1197 
1198 /** @} */
1199 
1200 
1201 
1202 /** \defgroup hwlocality_advanced_io Finding I/O objects
1203  * @{
1204  */
1205 
1206 /** \brief Get the first non-I/O ancestor object.
1207  *
1208  * Given the I/O object \p ioobj, find the smallest non-I/O ancestor
1209  * object. This object (normal or memory) may then be used for binding
1210  * because it has non-NULL CPU and node sets
1211  * and because its locality is the same as \p ioobj.
1212  *
1213  * \return a non-I/O object.
1214  *
1215  * \note This function cannot return \c NULL.
1216  *
1217  * \note The resulting object is usually a normal object but it could also
1218  * be a memory object (e.g. NUMA node) in future platforms if I/O objects
1219  * ever get attached to memory instead of CPUs.
1220  */
1221 static __hwloc_inline hwloc_obj_t
1222 hwloc_get_non_io_ancestor_obj(hwloc_topology_t topology __hwloc_attribute_unused,
1223                   hwloc_obj_t ioobj)
1224 {
1225   hwloc_obj_t obj = ioobj;
1226   while (obj && !obj->cpuset) {
1227     obj = obj->parent;
1228   }
1229   return obj;
1230 }
1231 
1232 /** \brief Get the next PCI device in the system.
1233  *
1234  * \return the first PCI device if \p prev is \c NULL.
1235  * \return the next PCI device if \p prev is not \c NULL.
1236  * \return \c NULL if there is no next PCI device.
1237  */
1238 static __hwloc_inline hwloc_obj_t
1239 hwloc_get_next_pcidev(hwloc_topology_t topology, hwloc_obj_t prev)
1240 {
1241   return hwloc_get_next_obj_by_type(topology, HWLOC_OBJ_PCI_DEVICE, prev);
1242 }
1243 
1244 /** \brief Find the PCI device object matching the PCI bus id
1245  * given domain, bus device and function PCI bus id.
1246  *
1247  * \return a matching PCI device object if any, \c NULL otherwise.
1248  */
1249 static __hwloc_inline hwloc_obj_t
1250 hwloc_get_pcidev_by_busid(hwloc_topology_t topology,
1251               unsigned domain, unsigned bus, unsigned dev, unsigned func)
1252 {
1253   hwloc_obj_t obj = NULL;
1254   while ((obj = hwloc_get_next_pcidev(topology, obj)) != NULL) {
1255     if (obj->attr->pcidev.domain == domain
1256     && obj->attr->pcidev.bus == bus
1257     && obj->attr->pcidev.dev == dev
1258     && obj->attr->pcidev.func == func)
1259       return obj;
1260   }
1261   return NULL;
1262 }
1263 
1264 /** \brief Find the PCI device object matching the PCI bus id
1265  * given as a string xxxx:yy:zz.t or yy:zz.t.
1266  *
1267  * \return a matching PCI device object if any, \c NULL otherwise.
1268  */
1269 static __hwloc_inline hwloc_obj_t
1270 hwloc_get_pcidev_by_busidstring(hwloc_topology_t topology, const char *busid)
1271 {
1272   unsigned domain = 0; /* default */
1273   unsigned bus, dev, func;
1274 
1275   if (sscanf(busid, "%x:%x.%x", &bus, &dev, &func) != 3
1276       && sscanf(busid, "%x:%x:%x.%x", &domain, &bus, &dev, &func) != 4) {
1277     errno = EINVAL;
1278     return NULL;
1279   }
1280 
1281   return hwloc_get_pcidev_by_busid(topology, domain, bus, dev, func);
1282 }
1283 
1284 /** \brief Get the next OS device in the system.
1285  *
1286  * \return the first OS device if \p prev is \c NULL.
1287  * \return the next OS device if \p prev is not \c NULL.
1288  * \return \c NULL if there is no next OS device.
1289  */
1290 static __hwloc_inline hwloc_obj_t
1291 hwloc_get_next_osdev(hwloc_topology_t topology, hwloc_obj_t prev)
1292 {
1293   return hwloc_get_next_obj_by_type(topology, HWLOC_OBJ_OS_DEVICE, prev);
1294 }
1295 
1296 /** \brief Get the next bridge in the system.
1297  *
1298  * \return the first bridge if \p prev is \c NULL.
1299  * \return the next bridge if \p prev is not \c NULL.
1300  * \return \c NULL if there is no next bridge.
1301  */
1302 static __hwloc_inline hwloc_obj_t
1303 hwloc_get_next_bridge(hwloc_topology_t topology, hwloc_obj_t prev)
1304 {
1305   return hwloc_get_next_obj_by_type(topology, HWLOC_OBJ_BRIDGE, prev);
1306 }
1307 
1308 /* \brief Checks whether a given bridge covers a given PCI bus.
1309  *
1310  * \return 1 if it covers, 0 if not.
1311  */
1312 static __hwloc_inline int
1313 hwloc_bridge_covers_pcibus(hwloc_obj_t bridge,
1314                unsigned domain, unsigned bus)
1315 {
1316   return bridge->type == HWLOC_OBJ_BRIDGE
1317     && bridge->attr->bridge.downstream_type == HWLOC_OBJ_BRIDGE_PCI
1318     && bridge->attr->bridge.downstream.pci.domain == domain
1319     && bridge->attr->bridge.downstream.pci.secondary_bus <= bus
1320     && bridge->attr->bridge.downstream.pci.subordinate_bus >= bus;
1321 }
1322 
1323 /** @} */
1324 
1325 
1326 
1327 #ifdef __cplusplus
1328 } /* extern "C" */
1329 #endif
1330 
1331 
1332 #endif /* HWLOC_HELPER_H */