Linux userland API discussions
 help / color / mirror / Atom feed
From: Jiri Pirko <jiri@resnulli.us>
To: linux-coco@lists.linux.dev
Cc: linux-kernel@vger.kernel.org, linux-api@vger.kernel.org,
	x86@kernel.org, kvm@vger.kernel.org, linux-doc@vger.kernel.org,
	jgg@ziepe.ca, david.quigley@intel.com, lukas@wunner.de,
	ksinyuk@kernel.org, rodrigo.vivi@intel.com,
	cedric.xing@intel.com, sameo@meta.com, drdeeglaze@gmail.com,
	James.Bottomley@HansenPartnership.com, aik@amd.com,
	jpiotrowski@nvidia.com, qinkun@google.com, pgonda@google.com,
	jic23@kernel.org, alistair.francis@wdc.com, ankita@nvidia.com,
	suzuki.poulose@arm.com, kas@kernel.org,
	rick.p.edgecombe@intel.com, elena.reshetova@intel.com,
	dave.hansen@linux.intel.com,
	sathyanarayanan.kuppuswamy@linux.intel.com, peter.fang@intel.com
Subject: [PATCH RFC 11/20] ctlv: Prepare runtime introspection
Date: Thu, 15 Oct 2026 16:50:44 +0200	[thread overview]
Message-ID: <20261015145053.3664509-12-jiri@resnulli.us> (raw)
In-Reply-To: <20261015145053.3664509-1-jiri@resnulli.us>

From: Jiri Pirko <jiri@nvidia.com>

Add handlers that expose registered families, effective operations and
message schemas through the CTLV root family.

The introspection object can be compile-checked here. CONFIG_CTLV remains
unwired until device dispatch completes the module.

Assisted-by: Cursor: GPT-5.6 Sol
Assisted-by: Cursor: Claude Opus 5.5
Signed-off-by: Jiri Pirko <jiri@nvidia.com>
---
 drivers/ctlv/Makefile        |   3 +-
 drivers/ctlv/introspection.c | 512 +++++++++++++++++++++++++++++++++++
 2 files changed, 514 insertions(+), 1 deletion(-)
 create mode 100644 drivers/ctlv/introspection.c

diff --git a/drivers/ctlv/Makefile b/drivers/ctlv/Makefile
index e631ccd68994..4d09fe26c1d0 100644
--- a/drivers/ctlv/Makefile
+++ b/drivers/ctlv/Makefile
@@ -1,4 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0
 obj-$(CONFIG_CTLV) += ctlv.o
 
-ctlv-y += blob.o blob_desc.o builder.o event.o message.o schema.o
+ctlv-y += blob.o blob_desc.o builder.o event.o introspection.o message.o \
+	  schema.o
diff --git a/drivers/ctlv/introspection.c b/drivers/ctlv/introspection.c
new file mode 100644
index 000000000000..7460dfa6d661
--- /dev/null
+++ b/drivers/ctlv/introspection.c
@@ -0,0 +1,512 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES
+ *
+ * Root-family discovery queries and their fragmentation.
+ */
+
+#include <linux/bitmap.h>
+#include <linux/bits.h>
+#include <linux/cleanup.h>
+#include <linux/ctlv.h>
+#include <linux/ctlv_gen.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+#include "device.h"
+#include "internal.h"
+#include "schema.h"
+
+/* A cursor packs the record and member where fragmentation resumes. */
+#define CTLV_INTROSPECTION_MEMBER_SHIFT 48
+#define CTLV_INTROSPECTION_RECORD_MASK GENMASK_ULL(CTLV_INTROSPECTION_MEMBER_SHIFT - 1, 0)
+
+struct ctlv_introspection_dump_position {
+	u64 record;
+	u32 member;
+};
+
+static void ctlv_introspection_dump_resume(struct ctlv_introspection_dump_position *pos, u64 cursor)
+{
+	pos->record = cursor & CTLV_INTROSPECTION_RECORD_MASK;
+	pos->member = cursor >> CTLV_INTROSPECTION_MEMBER_SHIFT;
+}
+
+static u64 ctlv_introspection_dump_cursor(const struct ctlv_introspection_dump_position *pos)
+{
+	return pos->record | (u64)pos->member << CTLV_INTROSPECTION_MEMBER_SHIFT;
+}
+
+/* The immutable root-first chain always has generation zero. */
+static bool ctlv_introspection_family_record(struct ctlv_cursor *reply, struct ctlv_cursor *entry,
+					     const struct ctlv_family *family,
+					     struct ctlv_introspection_dump_position *pos)
+{
+	const struct ctlv_family_schema *schema = family->schema;
+
+	if (!ctlv_family_entry_start(reply, entry))
+		return false;
+	if (pos->member == 0) {
+		if (!ctlv_family_id_put(entry, schema->family_id))
+			return false;
+		pos->member = 1;
+	}
+	if (pos->member == 1) {
+		if (!ctlv_family_name_put(entry, schema->name))
+			return false;
+		pos->member = 2;
+	}
+	ctlv_nest_end(entry);
+	pos->member = 0;
+	return true;
+}
+
+int ctlv_families_dump_query(void *priv, struct ctlv_op_ctx *ctx, const struct ctlv_cursor *request,
+			     struct ctlv_cursor *reply)
+{
+	const struct ctlv_family *family = ctx->family;
+	u32 levels = ctlv_family_levels(family);
+	struct ctlv_cursor entry;
+	struct ctlv_introspection_dump_position pos;
+
+	ctlv_introspection_dump_resume(&pos, ctlv_op_query_cursor(ctx));
+	for (; pos.record < levels; pos.record++) {
+		const struct ctlv_family *ancestor =
+			ctlv_family_ancestor(family, levels - 1 - pos.record);
+
+		if (!ctlv_introspection_family_record(reply, &entry, ancestor, &pos))
+			return ctlv_op_query_more(ctx, 0, ctlv_introspection_dump_cursor(&pos));
+	}
+	return ctlv_op_query_done(ctx, 0);
+}
+
+/* Snapshot published-op state and generation once per fragment. */
+struct ctlv_op_snapshot {
+	unsigned long *disabled;
+	u64 generation;
+};
+
+static int ctlv_introspection_snapshot_take(struct ctlv_device *ctlvdev,
+					    struct ctlv_op_snapshot *snapshot)
+{
+	if (ctlvdev->dynamic_ops_count) {
+		snapshot->disabled = bitmap_zalloc(ctlvdev->dynamic_ops_count, GFP_KERNEL);
+		if (!snapshot->disabled)
+			return -ENOMEM;
+	}
+	guard(spinlock)
+		(&ctlvdev->transition_lock);
+	snapshot->generation = ctlvdev->state_generation;
+	if (snapshot->disabled)
+		bitmap_copy(snapshot->disabled, ctlvdev->disabled, ctlvdev->dynamic_ops_count);
+	return 0;
+}
+
+static bool ctlv_introspection_op_disabled(const struct ctlv_op_snapshot *snapshot,
+					   const struct ctlv_op_entry *op)
+{
+	if (op->state_index == CTLV_OP_STATE_NONE)
+		return false;
+	return test_bit(op->state_index, snapshot->disabled);
+}
+
+/* Report effective lengths, including descendant fills. */
+enum ctlv_introspection_op_field {
+	CTLV_INTROSPECTION_OP_FIELD_ID,
+	CTLV_INTROSPECTION_OP_FIELD_TYPE,
+	CTLV_INTROSPECTION_OP_FIELD_AVAILABILITY,
+	CTLV_INTROSPECTION_OP_FIELD_DISABLED,
+	CTLV_INTROSPECTION_OP_FIELD_REQUEST_MAX_LEN,
+	CTLV_INTROSPECTION_OP_FIELD_ACTION_REPLY_MAX_LEN,
+	CTLV_INTROSPECTION_OP_FIELD_QUERY_REPLY_MIN_LEN,
+	CTLV_INTROSPECTION_OP_FIELD_QUERY_REPLY_SUGGESTED_LEN,
+	CTLV_INTROSPECTION_OP_FIELD_EVENT_RECORD_MAX_LEN,
+	CTLV_INTROSPECTION_OP_FIELD_FD_ACCESS,
+	CTLV_INTROSPECTION_OP_FIELD_END,
+};
+
+static bool ctlv_introspection_op_field_put(struct ctlv_cursor *entry,
+					    const struct ctlv_op_snapshot *snapshot,
+					    const struct ctlv_op_entry *op, u32 member)
+{
+	const struct ctlv_op_sizes *sizes = &op->sizes;
+
+	switch (member) {
+	case CTLV_INTROSPECTION_OP_FIELD_ID:
+		return ctlv_op_id_put(entry, op->schema->op_id);
+	case CTLV_INTROSPECTION_OP_FIELD_TYPE:
+		return ctlv_op_type_put(entry, op->schema->type);
+	case CTLV_INTROSPECTION_OP_FIELD_AVAILABILITY:
+		return ctlv_op_availability_put(entry, op->schema->availability);
+	case CTLV_INTROSPECTION_OP_FIELD_DISABLED:
+		return ctlv_op_disabled_put(entry, ctlv_introspection_op_disabled(snapshot, op));
+	case CTLV_INTROSPECTION_OP_FIELD_REQUEST_MAX_LEN:
+		return ctlv_op_request_max_len_put(entry, sizes->request_max_len);
+	case CTLV_INTROSPECTION_OP_FIELD_ACTION_REPLY_MAX_LEN:
+		/* Zero is a reply that acknowledges and no more. */
+		return ctlv_op_action_reply_max_len_put(entry, sizes->action_reply_max_len);
+	case CTLV_INTROSPECTION_OP_FIELD_QUERY_REPLY_MIN_LEN:
+		return ctlv_op_query_reply_min_len_put(entry, sizes->query_reply_min_len);
+	case CTLV_INTROSPECTION_OP_FIELD_QUERY_REPLY_SUGGESTED_LEN:
+		return ctlv_op_query_reply_suggested_len_put(entry,
+							     sizes->query_reply_suggested_len);
+	case CTLV_INTROSPECTION_OP_FIELD_FD_ACCESS:
+		return ctlv_op_fd_access_put(entry, op->schema->fd_access);
+	default:
+		return ctlv_op_event_record_max_len_put(entry, sizes->event_record_max_len);
+	}
+}
+
+static bool ctlv_introspection_op_field_applies(const struct ctlv_op_entry *op, u32 member)
+{
+	switch (member) {
+	case CTLV_INTROSPECTION_OP_FIELD_ID:
+	case CTLV_INTROSPECTION_OP_FIELD_TYPE:
+		return true;
+	case CTLV_INTROSPECTION_OP_FIELD_AVAILABILITY:
+	case CTLV_INTROSPECTION_OP_FIELD_DISABLED:
+	case CTLV_INTROSPECTION_OP_FIELD_REQUEST_MAX_LEN:
+	case CTLV_INTROSPECTION_OP_FIELD_FD_ACCESS:
+		return op->schema->type != CTLV_OP_TYPE_EVENT;
+	case CTLV_INTROSPECTION_OP_FIELD_ACTION_REPLY_MAX_LEN:
+		return op->schema->type == CTLV_OP_TYPE_ACTION;
+	case CTLV_INTROSPECTION_OP_FIELD_EVENT_RECORD_MAX_LEN:
+		return op->schema->type == CTLV_OP_TYPE_EVENT;
+	default:
+		return op->schema->type == CTLV_OP_TYPE_QUERY;
+	}
+}
+
+static bool ctlv_introspection_op_record(struct ctlv_cursor *reply, struct ctlv_cursor *entry,
+					 const struct ctlv_op_snapshot *snapshot,
+					 const struct ctlv_op_entry *op,
+					 struct ctlv_introspection_dump_position *pos)
+{
+	if (!ctlv_op_entry_start(reply, entry))
+		return false;
+	for (; pos->member < CTLV_INTROSPECTION_OP_FIELD_END; pos->member++) {
+		if (!ctlv_introspection_op_field_applies(op, pos->member))
+			continue;
+		if (!ctlv_introspection_op_field_put(entry, snapshot, op, pos->member))
+			return false;
+	}
+	ctlv_nest_end(entry);
+	pos->member = 0;
+	return true;
+}
+
+int ctlv_ops_dump_query(void *priv, struct ctlv_op_ctx *ctx, const struct ctlv_cursor *request,
+			struct ctlv_cursor *reply)
+{
+	struct ctlv_device *ctlvdev = ctx->ctlvdev;
+	struct ctlv_op_snapshot snapshot = {};
+	struct ctlv_cursor entry;
+	struct ctlv_introspection_dump_position pos;
+	int ret;
+
+	ret = ctlv_introspection_snapshot_take(ctlvdev, &snapshot);
+	if (ret)
+		return ret;
+	unsigned long *disabled __free(bitmap) = snapshot.disabled;
+
+	/* The published index is immutable, so a cursor into it holds. */
+	ctlv_introspection_dump_resume(&pos, ctlv_op_query_cursor(ctx));
+	for (; pos.record < ctlvdev->ops_count; pos.record++)
+		if (!ctlv_introspection_op_record(reply, &entry, &snapshot,
+						  &ctlvdev->ops[pos.record], &pos))
+			return ctlv_op_query_more(ctx, snapshot.generation,
+						  ctlv_introspection_dump_cursor(&pos));
+	return ctlv_op_query_done(ctx, snapshot.generation);
+}
+
+/* A leaf-count cursor permits a fragment boundary at any tree depth. */
+
+/* One nest of the description: the placement of one node of the schema. */
+struct ctlv_introspection_frame {
+	struct ctlv_cursor nest;
+	u64 attr_id;
+	bool open;
+	/* Which node this stands for, so that a re-entry can be measured. */
+	const struct ctlv_schema_tree *tree;
+	u32 node;
+};
+
+/* The frames outlive the walk, a fragment's open nests having to stay alive. */
+struct ctlv_introspection_walk {
+	const struct ctlv_family *family;
+	struct ctlv_cursor *root;
+	struct ctlv_introspection_frame *frames;
+	u64 ordinal;
+	u64 resume;
+	int error;
+};
+
+/* One member of one node, which is every leaf this reply has. */
+struct ctlv_introspection_field {
+	u64 attr_id;
+	u8 kind;
+	u64 value;
+};
+
+/* Three every node has, the widest bounds, a unit, a scale, a re-entry. */
+#define CTLV_INTROSPECTION_MAX_FIELDS 10
+
+struct ctlv_introspection_fields {
+	struct ctlv_introspection_field fields[CTLV_INTROSPECTION_MAX_FIELDS];
+	u32 fields_count;
+};
+
+static void ctlv_introspection_field(struct ctlv_introspection_fields *fields, u64 attr_id, u8 kind,
+				     u64 value)
+{
+	if (WARN_ON_ONCE(fields->fields_count == CTLV_INTROSPECTION_MAX_FIELDS))
+		return;
+	fields->fields[fields->fields_count++] = (struct ctlv_introspection_field){
+		.attr_id = attr_id,
+		.kind = kind,
+		.value = value,
+	};
+}
+
+static void ctlv_introspection_uint(struct ctlv_introspection_fields *fields, u64 attr_id,
+				    u64 value)
+{
+	ctlv_introspection_field(fields, attr_id, CTLV_SCHEMA_UINT, value);
+}
+
+/* Emit only constraints for this node's payload kind. */
+static void ctlv_introspection_node_fields(struct ctlv_introspection_fields *fields,
+					   const struct ctlv_node_schema *node, u32 reenters)
+{
+	ctlv_introspection_uint(fields, CTLV_ATTR_SCHEMA_ATTR_ID, node->attr_id);
+	ctlv_introspection_uint(fields, CTLV_ATTR_SCHEMA_KIND, node->kind);
+	ctlv_introspection_uint(fields, CTLV_ATTR_SCHEMA_MAX_OCCURRENCES, node->max_occurrences);
+
+	switch (node->kind) {
+	case CTLV_SCHEMA_UINT:
+	case CTLV_SCHEMA_SINT:
+	case CTLV_SCHEMA_ENUM:
+		ctlv_introspection_uint(fields, CTLV_ATTR_SCHEMA_MIN, node->range.min);
+		ctlv_introspection_uint(fields, CTLV_ATTR_SCHEMA_MAX, node->range.max);
+		break;
+	case CTLV_SCHEMA_FLAGS:
+		ctlv_introspection_uint(fields, CTLV_ATTR_SCHEMA_FLAGS_MASK, node->flags_mask);
+		break;
+	case CTLV_SCHEMA_STRING:
+	case CTLV_SCHEMA_BINARY:
+		ctlv_introspection_uint(fields, CTLV_ATTR_SCHEMA_MIN_LENGTH, node->length.min_len);
+		ctlv_introspection_uint(fields, CTLV_ATTR_SCHEMA_MAX_LENGTH, node->length.max_len);
+		break;
+	case CTLV_SCHEMA_BLOB:
+		ctlv_introspection_field(fields, CTLV_ATTR_SCHEMA_BLOB_DIRECTION, CTLV_SCHEMA_ENUM,
+					 node->blob.direction);
+		ctlv_introspection_uint(fields, CTLV_ATTR_SCHEMA_BLOB_BACKINGS,
+					node->blob.backings);
+		ctlv_introspection_uint(fields, CTLV_ATTR_SCHEMA_BLOB_MAX_LENGTH,
+					node->blob.max_len);
+		ctlv_introspection_uint(fields, CTLV_ATTR_SCHEMA_BLOB_ALIGNMENT,
+					node->blob.alignment);
+		break;
+	default:
+		break;
+	}
+
+	if (node->unit != CTLV_UNIT_NONE) {
+		ctlv_introspection_uint(fields, CTLV_ATTR_SCHEMA_UNIT, node->unit);
+		ctlv_introspection_field(fields, CTLV_ATTR_SCHEMA_SCALE, CTLV_SCHEMA_SINT,
+					 node->scale);
+	}
+	if (reenters)
+		ctlv_introspection_uint(fields, CTLV_ATTR_SCHEMA_REENTERS, reenters);
+}
+
+/* A nest is opened by the first thing written inside it. */
+static bool ctlv_introspection_open(struct ctlv_introspection_walk *walk, u32 depth)
+{
+	struct ctlv_introspection_frame *frame = &walk->frames[depth];
+	struct ctlv_cursor *up = walk->root;
+
+	if (frame->open)
+		return true;
+	if (depth) {
+		if (!ctlv_introspection_open(walk, depth - 1))
+			return false;
+		up = &walk->frames[depth - 1].nest;
+	}
+	if (!ctlv_nest_start(up, &frame->nest, frame->attr_id, CTLV_SCHEMA_NEST))
+		return false;
+	frame->open = true;
+	return true;
+}
+
+/* Skipped leaves advance the ordinal; a failed write remains the resume point. */
+static bool ctlv_introspection_put(struct ctlv_introspection_walk *walk, u32 depth,
+				   const struct ctlv_introspection_field *field)
+{
+	struct ctlv_cursor *nest = &walk->frames[depth].nest;
+	bool ok;
+
+	if (walk->ordinal < walk->resume) {
+		walk->ordinal++;
+		return true;
+	}
+	if (!ctlv_introspection_open(walk, depth))
+		return false;
+	if (field->kind == CTLV_SCHEMA_SINT)
+		ok = ctlv_put_s64(nest, field->attr_id, field->kind, field->value);
+	else
+		ok = ctlv_put_u64(nest, field->attr_id, field->kind, field->value);
+	if (!ok)
+		return false;
+	walk->ordinal++;
+	return true;
+}
+
+/* Re-entry targets an ancestor with the same child range. */
+static u32 ctlv_introspection_reenters(struct ctlv_introspection_walk *walk, u32 depth,
+				       const struct ctlv_schema_tree *tree,
+				       const struct ctlv_node_schema *node)
+{
+	u32 levels;
+
+	for (levels = 1; levels <= depth; levels++) {
+		const struct ctlv_introspection_frame *up = &walk->frames[depth - levels];
+
+		if (up->tree == tree &&
+		    tree->node_schemas[up->node].first_child == node->first_child)
+			return levels;
+	}
+	/* A back edge to nothing is metadata this core cannot describe. */
+	WARN_ON_ONCE(1);
+	walk->error = -EINVAL;
+	return 0;
+}
+
+static bool ctlv_introspection_node(struct ctlv_introspection_walk *walk, u32 depth,
+				    const struct ctlv_schema_tree *tree, u32 index);
+
+/* Filled stub members inherit the stub placement's optionality. */
+static bool ctlv_introspection_children(struct ctlv_introspection_walk *walk, u32 depth,
+					const struct ctlv_schema_tree *tree, u32 index,
+					bool force_optional)
+{
+	const struct ctlv_node_schema *node = &tree->node_schemas[index];
+	u32 i;
+
+	if (WARN_ON_ONCE(depth + 1 >= CTLV_MAX_NEST_DEPTH)) {
+		/* Deeper than a specification may declare, so unreachable. */
+		walk->error = -EINVAL;
+		return false;
+	}
+	for (i = 0; i < node->children_count; i++) {
+		u32 child = node->first_child + i;
+		bool required = !force_optional && i < node->required_children_count;
+
+		walk->frames[depth + 1] = (struct ctlv_introspection_frame){
+			.attr_id = required ? CTLV_ATTR_SCHEMA_ATTRS :
+					      CTLV_ATTR_SCHEMA_OPTIONAL_ATTRS,
+			.tree = tree,
+			.node = child,
+		};
+		if (!ctlv_introspection_node(walk, depth + 1, tree, child))
+			return false;
+	}
+	for (i = 0; i < tree->stub_schemas_count; i++) {
+		const struct ctlv_stub_schema *stub_schema = &tree->stub_schemas[i];
+		const struct ctlv_stub_fill_schema *fill_schema;
+
+		if (stub_schema->node != index)
+			continue;
+		fill_schema =
+			ctlv_schema_fill(walk->family, stub_schema->family_id, stub_schema->set_id);
+		/* Nothing of this chain filled it, so it contributes none. */
+		if (!fill_schema)
+			continue;
+		/* A fill schema's root is the stub, its children the members. */
+		if (!ctlv_introspection_children(walk, depth, &fill_schema->members, 0,
+						 force_optional || stub_schema->optional))
+			return false;
+	}
+	return true;
+}
+
+/* One node, into the nest that stands for it. */
+static bool ctlv_introspection_node(struct ctlv_introspection_walk *walk, u32 depth,
+				    const struct ctlv_schema_tree *tree, u32 index)
+{
+	const struct ctlv_node_schema *node = &tree->node_schemas[index];
+	struct ctlv_introspection_fields fields = {};
+	u32 reenters = 0;
+	u32 i;
+
+	if (node->reenters) {
+		reenters = ctlv_introspection_reenters(walk, depth, tree, node);
+		if (!reenters)
+			return false;
+	}
+	ctlv_introspection_node_fields(&fields, node, reenters);
+	for (i = 0; i < fields.fields_count; i++)
+		if (!ctlv_introspection_put(walk, depth, &fields.fields[i]))
+			return false;
+	/*
+	 * A node that re-enters a level says so instead of listing that level's
+	 * members again, which is what makes an unbounded nesting describable.
+	 */
+	if (!node->reenters && !ctlv_introspection_children(walk, depth, tree, index, false))
+		return false;
+	if (walk->frames[depth].open)
+		ctlv_nest_end(&walk->frames[depth].nest);
+	return true;
+}
+
+/* One of the two messages of an op, the root of it being a node like any. */
+static bool ctlv_introspection_message(struct ctlv_introspection_walk *walk, u64 attr_id,
+				       const struct ctlv_schema_tree *tree)
+{
+	/* An op without such a message has nothing to describe. */
+	if (!tree->node_schemas)
+		return true;
+	walk->frames[0] = (struct ctlv_introspection_frame){
+		.attr_id = attr_id,
+		.tree = tree,
+	};
+	return ctlv_introspection_node(walk, 0, tree, 0);
+}
+
+int ctlv_op_schema_get_query(void *priv, struct ctlv_op_ctx *ctx, const struct ctlv_cursor *request,
+			     struct ctlv_cursor *reply)
+{
+	struct ctlv_introspection_walk walk = {
+		.family = ctx->family,
+		.root = reply,
+		.resume = ctlv_op_query_cursor(ctx),
+	};
+	const struct ctlv_op_entry *op;
+
+	/* Describe published ops regardless of current dynamic state. */
+	op = ctlv_op_find(ctx->ctlvdev, ctlv_op_schema_get_op_id_get(request));
+	if (!op)
+		return -EOPNOTSUPP;
+
+	/*
+	 * One frame per level the reply may nest, too many for the stack: a
+	 * description is one level deeper than the message it describes.
+	 */
+	struct ctlv_introspection_frame *frames __free(kfree) =
+		kcalloc(CTLV_MAX_NEST_DEPTH, sizeof(*frames), GFP_KERNEL);
+
+	if (!frames)
+		return -ENOMEM;
+	walk.frames = frames;
+
+	if (!ctlv_introspection_message(&walk, CTLV_ATTR_SCHEMA_REQUEST, &op->schema->request) ||
+	    !ctlv_introspection_message(&walk, CTLV_ATTR_SCHEMA_REPLY, &op->schema->reply)) {
+		if (walk.error)
+			return walk.error;
+		/* A schema is immutable, so there is nothing to be stale. */
+		return ctlv_op_query_more(ctx, 0, walk.ordinal);
+	}
+	return ctlv_op_query_done(ctx, 0);
+}
-- 
2.56.0


  parent reply	other threads:[~2026-10-15 14:51 UTC|newest]

Thread overview: 21+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-15 14:50 [PATCH RFC 00/20] attestation: Add a common subsystem and TDX support Jiri Pirko
2026-10-15 14:50 ` [PATCH RFC 01/20] ctlv: Add the schema language and generator Jiri Pirko
2026-10-15 14:50 ` [PATCH RFC 02/20] ctlv: Add the UAPI wire format and family API Jiri Pirko
2026-10-15 14:50 ` [PATCH RFC 03/20] ctlv: Add the root family specification and generated files Jiri Pirko
2026-10-15 14:50 ` [PATCH RFC 04/20] Documentation: ctlv: Add userspace ABI Jiri Pirko
2026-10-15 14:50 ` [PATCH RFC 05/20] ctlv: Prepare message input handling Jiri Pirko
2026-10-15 14:50 ` [PATCH RFC 06/20] ctlv: Prepare blob descriptor validation Jiri Pirko
2026-10-15 14:50 ` [PATCH RFC 07/20] ctlv: Prepare schema validation Jiri Pirko
2026-10-15 14:50 ` [PATCH RFC 08/20] ctlv: Prepare message output handling Jiri Pirko
2026-10-15 14:50 ` [PATCH RFC 09/20] ctlv: Prepare VA blob handling Jiri Pirko
2026-10-15 14:50 ` [PATCH RFC 10/20] ctlv: Prepare event queue support Jiri Pirko
2026-10-15 14:50 ` Jiri Pirko [this message]
2026-10-15 14:50 ` [PATCH RFC 12/20] ctlv: Add device and operation dispatch Jiri Pirko
2026-10-15 14:50 ` [PATCH RFC 13/20] Documentation: ctlv: Describe how to write a family Jiri Pirko
2026-10-15 14:50 ` [PATCH RFC 14/20] attestation: Add the base family specification and generated files Jiri Pirko
2026-10-15 14:50 ` [PATCH RFC 15/20] attestation: Prepare provider and instance core Jiri Pirko
2026-10-15 14:50 ` [PATCH RFC 16/20] attestation: Prepare measurement register journal Jiri Pirko
2026-10-15 14:50 ` [PATCH RFC 17/20] attestation: Add CTLV operation handling Jiri Pirko
2026-10-15 14:50 ` [PATCH RFC 18/20] tdx-guest: Expose the native attestation service API Jiri Pirko
2026-10-15 14:50 ` [PATCH RFC 19/20] attestation: Add the TDX family specification and generated files Jiri Pirko
2026-10-15 14:50 ` [PATCH RFC 20/20] attestation: Add the TDX family implementation Jiri Pirko

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261015145053.3664509-12-jiri@resnulli.us \
    --to=jiri@resnulli.us \
    --cc=James.Bottomley@HansenPartnership.com \
    --cc=aik@amd.com \
    --cc=alistair.francis@wdc.com \
    --cc=ankita@nvidia.com \
    --cc=cedric.xing@intel.com \
    --cc=dave.hansen@linux.intel.com \
    --cc=david.quigley@intel.com \
    --cc=drdeeglaze@gmail.com \
    --cc=elena.reshetova@intel.com \
    --cc=jgg@ziepe.ca \
    --cc=jic23@kernel.org \
    --cc=jpiotrowski@nvidia.com \
    --cc=kas@kernel.org \
    --cc=ksinyuk@kernel.org \
    --cc=kvm@vger.kernel.org \
    --cc=linux-api@vger.kernel.org \
    --cc=linux-coco@lists.linux.dev \
    --cc=linux-doc@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=lukas@wunner.de \
    --cc=peter.fang@intel.com \
    --cc=pgonda@google.com \
    --cc=qinkun@google.com \
    --cc=rick.p.edgecombe@intel.com \
    --cc=rodrigo.vivi@intel.com \
    --cc=sameo@meta.com \
    --cc=sathyanarayanan.kuppuswamy@linux.intel.com \
    --cc=suzuki.poulose@arm.com \
    --cc=x86@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox