blob: b3caa3ca7e97b0967168d3f7f38a60c796d9d8a1 [file] [log] [blame]
#include <lib/system-topology.h>
#include <zircon/errors.h>
namespace system_topology {
namespace {
constexpr size_t kMaxTopologyDepth = 20;
inline void ValidationError(int index, const char* message) {
printf("Error validating topology at node %d : %s \n", index, message);
}
} // namespace
zx_status_t Graph::Update(zbi_topology_node_t* flat_nodes, size_t count) {
if (flat_nodes == nullptr || count == 0 || !Validate(flat_nodes, static_cast<int>(count))) {
return ZX_ERR_INVALID_ARGS;
}
if (nodes_.get() != nullptr) {
return ZX_ERR_ALREADY_EXISTS;
}
fbl::AllocChecker checker;
nodes_.reset(new (&checker) Node[count]{{}});
if (!checker.check()) {
return ZX_ERR_NO_MEMORY;
}
Node* node = nullptr;
zbi_topology_node_t* flat_node = nullptr;
for (size_t i = 0; i < count; ++i) {
flat_node = &flat_nodes[i];
node = &nodes_[i];
node->entity_type = flat_node->entity_type;
// Copy info.
switch (node->entity_type) {
case ZBI_TOPOLOGY_ENTITY_PROCESSOR:
node->entity.processor = flat_node->entity.processor;
processors_.push_back(node, &checker);
if (!checker.check()) {
nodes_.reset(nullptr);
return ZX_ERR_NO_MEMORY;
}
for (int i = 0; i < node->entity.processor.logical_id_count; ++i) {
processors_by_logical_id_.insert(node->entity.processor.logical_ids[i],
node, &checker);
if (!checker.check()) {
nodes_.reset(nullptr);
return ZX_ERR_NO_MEMORY;
}
}
break;
case ZBI_TOPOLOGY_ENTITY_CLUSTER:
node->entity.cluster = flat_node->entity.cluster;
break;
case ZBI_TOPOLOGY_ENTITY_NUMA_REGION:
node->entity.numa_region = flat_node->entity.numa_region;
break;
default:
// Other types don't have attached info.
break;
}
if (flat_node->parent_index != ZBI_TOPOLOGY_NO_PARENT) {
// Validation should have prevented this.
ZX_DEBUG_ASSERT_MSG(flat_node->parent_index >= 0 && flat_node->parent_index < count,
"parent_index out of range: %u\n", flat_node->parent_index);
node->parent = &nodes_[flat_node->parent_index];
node->parent->children.push_back(node, &checker);
if (!checker.check()) {
nodes_.reset(nullptr);
return ZX_ERR_NO_MEMORY;
}
}
}
return ZX_OK;
}
bool Graph::Validate(zbi_topology_node_t* nodes, int count) const {
uint16_t parents[kMaxTopologyDepth];
for (size_t i = 0; i < kMaxTopologyDepth; ++i) {
parents[i] = ZBI_TOPOLOGY_NO_PARENT;
}
uint8_t current_type = ZBI_TOPOLOGY_ENTITY_UNDEFINED;
int current_depth = 0;
zbi_topology_node_t* node;
for (int current_index = count - 1; current_index >= 0; current_index--) {
node = &nodes[current_index];
if (current_type == ZBI_TOPOLOGY_ENTITY_UNDEFINED) {
current_type = node->entity_type;
}
if (current_type != node->entity_type) {
if (current_index == parents[current_depth]) {
// If the type changes then it should be the parent of the
// previous level.
current_depth++;
if (current_depth == kMaxTopologyDepth) {
ValidationError(current_index,
"Structure is too deep, we only support 20 levels.");
return false;
}
} else if (node->entity_type == ZBI_TOPOLOGY_ENTITY_PROCESSOR) {
// If it isn't the parent of the previous level, but it is a process than we have
// encountered a new branch and should start walking from the bottom again.
// Clear the parent index's for all levels but the top, we want to ensure that the
// top level of the new branch reports to the same parent as we do.
for (int i = current_depth - 1; i >= 0; --i) {
parents[i] = ZBI_TOPOLOGY_NO_PARENT;
}
current_depth = 0;
} else {
// Otherwise the structure is incorrect.
ValidationError(current_index,
"Graph is not stored in correct order, with children adjacent to "
"parents");
return false;
}
current_type = node->entity_type;
}
if (parents[current_depth] == ZBI_TOPOLOGY_NO_PARENT) {
parents[current_depth] = node->parent_index;
} else if (parents[current_depth] != node->parent_index) {
ValidationError(current_index, "Parents at level do not match.");
return false;
}
// Ensure that all leaf nodes are processors.
if (current_depth == 0 && node->entity_type != ZBI_TOPOLOGY_ENTITY_PROCESSOR) {
ValidationError(current_index, "Encountered a leaf node that isn't a processor.");
return false;
}
// Ensure that all processors are leaf nodes.
if (current_depth != 0 && node->entity_type == ZBI_TOPOLOGY_ENTITY_PROCESSOR) {
ValidationError(current_index, "Encountered a processor that isn't a leaf node.");
return false;
}
// By the time we reach the first parent we should be at the maximum depth and have no
// parents defined.
if (current_index == 0 && parents[current_depth] != ZBI_TOPOLOGY_NO_PARENT &&
(current_depth == kMaxTopologyDepth - 1 ||
parents[current_depth + 1] == ZBI_TOPOLOGY_NO_PARENT)) {
ValidationError(current_index, "Top level of tree should not have a parent");
return false;
}
}
return true;
}
} // namespace system_topology