Re: [PATCH RFC 04/20] Documentation: ctlv: Add userspace ABI
From: Jiri Pirko
Date: Tue Nov 03 2026 - 06:10:09 EST
Thu, Oct 29, 2026 at 07:53:11PM +0100, sergii.dmytruk@xxxxxxxxx wrote:
>On Thu, Oct 15, 2026 at 04:50:37PM +0200, Jiri Pirko wrote:
>> From: Jiri Pirko <jiri@xxxxxxxxxx>
>>
>> Describe the CTLV transport, discovery, wire format and userspace
>> compatibility rules.
>>
>> Assisted-by: Cursor: GPT-5.6 Sol
>> Assisted-by: Cursor: Claude Opus 5.5
>> Signed-off-by: Jiri Pirko <jiri@xxxxxxxxxx>
>> ---
>> Documentation/userspace-api/ctlv.rst | 507 ++++++++++++++++++++++++++
>> Documentation/userspace-api/index.rst | 1 +
>> MAINTAINERS | 1 +
>> 3 files changed, 509 insertions(+)
>> create mode 100644 Documentation/userspace-api/ctlv.rst
>>
>> diff --git a/Documentation/userspace-api/ctlv.rst b/Documentation/userspace-api/ctlv.rst
>> new file mode 100644
>> index 000000000000..5ebca3d4d94a
>> --- /dev/null
>> +++ b/Documentation/userspace-api/ctlv.rst
>
>[...]
>
>> +Errors
>> +------
>> +
>> +A failed operation returns a negative errno. Where the failure is a rule the
>> +message broke and the caller offered a reply buffer, CTLV also writes
>> +error information there in TLV form: a ``validation-reason`` value, a
>> +human-readable message, the offset of the offending attribute, the offset of
>> +the nest it was in, the ID of a missing attribute, and for a stale query the
>> +expected and the current generation. A buffer too small leaves it unwritten;
>> +the errno is authoritative either way.
>> +
>> +What the transport answers with, as against a family's own callback:
>
>It seems like this mixes results of several syscalls (e.g., `open()` and
>`ioctl()`). Does it?
Right, will split.
>
>> +
>> +``ENOTTY``
>> + Not ``CTLV_IOC_OP_EXEC``; this transport has one ioctl.
>> +
>> +``EINVAL``
>> + A reserved field was not zero, ``op_id`` was zero, ``generation`` or
>> + ``cursor`` was set on something that is not a query, or the message broke
>> + a rule of the format or of the schema.
>> +
>> +``EFAULT``
>> + The structure, the request or the reply was not accessible.
>> +
>> +``EOPNOTSUPP``
>> + The device does not publish the operation, publishes it but has it
>> + disabled, or the operation is an event, which is read rather than executed.
>> +
>> +``EACCES``
>> + The fd lacks the operation's declared authority, or an ``O_WRONLY`` open
>> + was attempted.
>> +
>> +``EMSGSIZE``
>> + ``reply_len`` is smaller than the smallest fragment the operation can
>> + answer with. Nothing was executed.
>> +
>> +``ESTALE``
>> + A continuation whose ``generation`` no longer matches. Start the query
>> + again.
>> +
>> +``ENODEV``
>> + The device was unregistered. Descriptors stay open and every operation on
>> + them fails this way.
>> +
>> +``EBADF``, ``EIO``
>> + A blob descriptor named something that is not a file, or the file refused
>> + the transfer.
>> +
>> +``ENOMEM``
>> + The kernel could not allocate what the operation needed.
>> +
>> +``read()`` answers ``EAGAIN`` when no record is queued on a non-blocking
>> +description, ``EINTR`` when a blocking read was interrupted, ``EMSGSIZE`` when
>> +the buffer cannot hold the record at the head of the queue, and ``ENODEV``
>> +once a removed device's queue has been drained.
>
>[...]