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