Skip to content
Haiku OSDeep Dive Published Updated 6 min readViews unavailable

Haiku CPU Topology: Enumerating Packages, Cores, and SMT Nodes

Use Haiku's get_cpu_topology_info API carefully: inspect node types and levels, handle its count-and-fetch pattern, and avoid inventing CPU mappings.

CPU inventory is not the same as CPU topology. A machine may expose multiple logical processors, physical cores, simultaneous-multithreading (SMT) relationships, and packages. Software that needs to display or reason about those relationships should not infer them from a CPU count or assume that an operating system’s CPU index is a hardware-core identifier.

Haiku exposes a topology query through get_cpu_topology_info() in <OS.h>. The public interface describes a cpu_topology_node_info record containing an id, a type, a level, and a type-specific union. Its topology types include root, SMT, core, and package. The header marks this API experimental, so applications should isolate its use and revalidate it against each supported Haiku SDK. It is useful for inventory and diagnostics, but its record fields are narrower than a complete cross-platform topology database: callers must check the actual type before reading the corresponding union member and must not invent relationships the structure does not provide.

What the record contains

The root payload identifies a CPU platform. A package payload provides a vendor and cache-line size. A core payload provides a model and default frequency. B_TOPOLOGY_SMT is a topology type, but the current public record has no dedicated SMT payload. The id and level fields are present on every node; they are not named as a logical CPU index, package number, or portable parent pointer.

That distinction matters when writing an inventory UI. A B_TOPOLOGY_CORE record gives access to data.core; it does not, by itself, report the current frequency of every hardware thread. The separate cpu_info interface reports per-CPU activity, enabled state, and current frequency for a requested CPU range. The public declarations do not promise that a topology node’s id is interchangeable with the index passed to get_cpu_info(). Treat these as separate views unless a target-specific contract explicitly establishes a mapping.

The names also describe different kinds of information. B_TOPOLOGY_PACKAGE identifies a package-level node, not a socket identifier guaranteed to match firmware labels. B_TOPOLOGY_CORE exposes the core’s default frequency, not a promise about current boost or power-state frequency. The public field is a uint64, but the header does not annotate a unit; a UI should preserve the raw value until its target SDK’s contract confirms the display unit. Topology is useful context for a diagnostic tool, but it should not be turned into a hard-coded scheduler policy based only on these fields.

Follow the two-call enumeration pattern

Haiku’s own compatibility implementation first calls get_cpu_topology_info(NULL, &count), allocates storage for the reported number of nodes, and calls the function again with the array. A small diagnostic can follow that pattern while checking both status values and ensuring that it never reads more records than it allocated:

#include <OS.h>

#include <new>
#include <stdio.h>

status_t PrintCpuTopology()
{
    uint32 count = 0;
    status_t status = get_cpu_topology_info(NULL, &count);
    if (status != B_OK)
        return status;

    if (count == 0) {
        puts("Haiku returned no topology nodes");
        return B_OK;
    }

    cpu_topology_node_info* nodes =
        new(std::nothrow) cpu_topology_node_info[count];
    if (nodes == NULL)
        return B_NO_MEMORY;

    uint32 returnedCount = count;
    status = get_cpu_topology_info(nodes, &returnedCount);
    if (status == B_OK && returnedCount <= count) {
        for (uint32 i = 0; i < returnedCount; ++i) {
            const cpu_topology_node_info& node = nodes[i];
            printf("node id=%lu type=%d level=%lu",
                (unsigned long)node.id, (int)node.type,
                (unsigned long)node.level);

            switch (node.type) {
                case B_TOPOLOGY_ROOT:
                    printf(" platform=%d", (int)node.data.root.platform);
                    break;
                case B_TOPOLOGY_PACKAGE:
                    printf(" vendor=%d cache-line=%lu",
                        (int)node.data.package.vendor,
                        (unsigned long)node.data.package.cache_line_size);
                    break;
                case B_TOPOLOGY_CORE:
                    printf(" model=%lu default-frequency=%llu",
                        (unsigned long)node.data.core.model,
                        (unsigned long long)
                            node.data.core.default_frequency);
                    break;
                case B_TOPOLOGY_SMT:
                case B_TOPOLOGY_UNKNOWN:
                default:
                    break;
            }
            putchar('\n');
        }
    } else if (status == B_OK) {
        status = B_BAD_DATA;
    }

    delete[] nodes;
    return status;
}

This is a diagnostic example rather than a complete hardware policy. It follows Haiku’s in-tree count-then-fetch usage, handles allocation failure, and switches on the node type before accessing the union. If the second call reports an error or a count larger than the allocated capacity, it refuses to walk the buffer. A production component should also decide how to surface the returned status and should be compiled against the Haiku SDK it actually targets.

Keep CPU indices and topology records separate

Use get_system_info() when the question is simply how many CPUs Haiku reports. Use get_cpu_info(firstCPU, cpuCount, ...) when the application needs per-CPU status fields, such as enabled, active_time, or current_frequency. Use get_cpu_topology_info() when the question concerns the platform’s topology-node inventory and the package/core fields represented by that API.

Do not zip the two arrays together by position without a documented mapping. The topology structure exposes node IDs and levels; the cpu_info call accepts a starting CPU index and count. Similar integer types do not make their namespaces equivalent. Haiku’s current kernel implementation descends to an SMT-level node when resolving a logical processor ID for per-CPU topology data, but that implementation detail is not a promise that every topology node ID is a CPU index. Because the public API is experimental, an application should pair these views only after validating the mapping for the exact Haiku release and architecture it supports.

This also avoids a frequency mistake. The topology core field is default_frequency; cpu_info.current_frequency is the separate current-frequency field. Neither should be relabeled as the other. A user-facing tool can show a core’s default value alongside the current-frequency readings it obtains through the per-CPU API, but it should retain the data source and scope in its labels.

Treat topology as observed system data

Hardware configurations vary. A virtual machine may expose a different topology from its host, firmware or a hypervisor may omit details, and a given processor architecture may populate fields differently. The interface defines an unknown type as well as known types, so defensive code should tolerate records it does not recognize rather than reading the union as if every entry were a core.

For a support report, capture the Haiku version and architecture with the returned node type, ID, level, and type-specific fields. Compare outputs only when their environments are comparable; a VM’s virtual CPU layout is not evidence of the host’s physical socket or core arrangement. If a field is unavailable, show it as unavailable instead of guessing from the count.

Topology queries are snapshots, not instructions to pin threads or guarantee performance. Frequency can change, virtualized topology can be synthesized, and concurrent changes to enabled CPUs should be considered when combining topology with live per-CPU data. Requery when freshness matters, and keep diagnostic collection separate from scheduling decisions.

A practical review checklist

Before shipping a Haiku topology viewer, check that it obtains the node count before allocating, handles both API return statuses, bounds iteration by the allocated capacity, reads union fields only after checking type, and distinguishes default_frequency from current_frequency. Verify the names and fields in the SDK headers used to build the application, because public source trees and installed target SDKs can differ by release.

Most importantly, do not present every node.id as a logical CPU number or infer parent-child links from its numeric value. Haiku’s current kernel maps logical processor IDs through SMT-level nodes, but root, package, and core IDs have different topology roles. The public structure carries a type and level but no explicit parent-ID field. If an application needs a richer tree, establish that representation from a documented target-specific source and test it on each supported architecture rather than filling in missing edges by assumption.

Related:

Sources:

Comments