bug-hurd
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [RFC: drm server] limitations of MiG for ioctls


From: Sergey Bugaev
Subject: Re: [RFC: drm server] limitations of MiG for ioctls
Date: Tue, 5 Nov 2024 09:03:09 +0300

On Mon, Nov 4, 2024 at 10:44 AM Damien Zammit wrote:
> I am currently attempting to implement a drm server to provide
> a way to use libdrm with multiboot framebuffer exposed by new device(mbinfo).
>
> I am running into a problem that I am unable to implement a compatible
> ioctl api because of the layout of the structures.

Thanks for writing this down. I will repeat the same things I said on IRC:

> I would prefer to reuse the same api as drm ioctls rather than implement
> a modified version using traditional RPCs with many arguments.
> This is because libdrm would need to be modified substantially and I don't 
> want
> to clutter the client with more parameters and conditional code.

I understand that motivation, but the requirements about more complex
structs that you describe below are really not compatible with how MIG
structs -- or glibc/Hurd ioctls -- can be used.

> The main problem is that a few of the OUT ioctls expect the server to copy 
> data
> through the RPC, and MiG is confused by nested structs, it doesn't seem to
> support something like:
>
>         type drm_version_t = struct {
>                 int foo;
>                 int bar_length;
>                 data_t bar;
>         };

As mentioned on IRC, I'm surprised this doesn't immediately produce an
error. The logic for that seems to be in place:

    if (!t->itInLine) {
        error("Type %s must be inline\n", $2);
    }

Other than the error that should be there but isn't, it cannot work,
since these C-like structs are still desugared to classic structs,
i.e. just small fixed-size arrays of integers. Such a structure still
generates a single "data element" in a message body.

> You _can_ specify individual parameters in the routine like so:
>
>         routine drm_version (
>                 port: drm_t;
>                 foo: int;
>                 out bar: data_t SCP);
>
> but then the bar_length parameter comes AFTER the bar parameter,
> and has type unsigned int, (not int),

That's the way it works -- an array parameter in MIG defs maps to a
pointer + size pair in C, and size is of type mach_msg_type_number_t.

This is the part that could be tweaked most easily, if there was a
real need -- we could add a new MIG option or something. But I don't
think it would actually help much with your use case.

> and you cannot seem to pack the whole thing
> into a struct compatible with an ioctl like:
>
>         routine drm_version (
>                 port: drm_t;
>                 out bar: drm_version_t);

Yes, so none of this would work with ioctls at all. ioctls support
passing (small arrays of) basic integer types in and out, which is
again enough for simple structures. And indeed basic integer types and
simple structures are what ioctls work on, traditionally, on Unix.

It is not feasible to extend the ioctls mechanism for passing OOL
memory, because:

1. the ioctl ID encoding space is fully used already -- all the bits
in an ioctl ID are meaningful, so there'd be nowhere to pack the new
info;

2. where would the received pointer point? -- in other words, where
would the received data go? In Mach IPC, the received OOL data (or
port arrays) just appears as new pages somewhere in the receiver's
address space (vm_map). In Unix APIs something like that never
happens; the caller always has to provide its own buffer that the data
gets written over. Compare Hurd's io_read vs Unix read for example.

So in your case, it's likely that the caller has to provide, as input
to the ioctl, a pointer to where it would like the 'data_t bar' to be
written out to, and then Linux would write the actual 'bar' data into
the pointed-to memory. And 'bar_length' is likely in-out, signifying
the buffer length on 'in', and the used length on 'out'. Right?

It is very possible to implement this behavior on top of MIG routines.
But it is way too complex to try and support this in ioctls. We would
need to somehow encode where in the structure the size and the pointer
are... No, this just can't happen.

> How do I solve this?  Can we extend MiG to be smarter about nested structures 
> when
> data needs to be transferred within structs?  How do we solve the ordering 
> problem of
> the *_length parameter?

At least for the calls that have more complex behavior wrt memory (and
not just passing bundles of integers), don't try to make them into
ioctls, make them real full MIG routines, and use them as such from
the client side. Is it just drm_version? If so, perhaps it could just
always return some hardcoded values client-side, without making any
RPCs at all.

Sergey



reply via email to

[Prev in Thread] Current Thread [Next in Thread]