[PATCH RFC v2 00/16] drm/vkms: ConfigFS interface
Louis Chauvet
louis.chauvet at bootlin.com
Tue Dec 17 16:42:56 UTC 2024
Hi,
> > Hi all,
> >
> > I am also currently working on MST emulation for VKMS. If someone can read
> > what I already did and at tell me if my implementation seems on the right
> > track it could be nice.
> >
> > The current status is not very advanced: I can emulate a mst HUB, but not
> > a screen. I am currently working on properly emulating the HUB by using an
> > other hub.
> >
> > You can find the branch for this work here:
> > https://gitlab.freedesktop.org/louischauvet/kernel/-/tree/b4/vkms-mst
>
> I think this is exactly the kind of things where we'll want eBPF I
> think. There's no way you'll be able to model each possible test
> scenarios for MST through configfs, even more so with a stable
> interface.
I just spent some time to think about it. I agree that eBPF seems
applicable: we want to allows userspace to emulate any MST device, and we
don't want to create huge uAPI + whole emulation in the kernel.
As most of the protocol is similar accros device kind, I currently built
my code around the struct vkms_mst_sideband_helpers to specify only the
changed behavior (this way the "write to registers" "parse command"... is
only done in one place). The choice of function is not definitive at all
(it was just practical at this moment).
With eBPF, I know three different way to attach programs to the kernel:
- Using a kprobe/attaching to a function: I can change the behavior of
all the device, there is no way "attach prog1 to hub" and "attach prog2
to screen1", it will be "attach prog1 to the function
`vkms_mst_emulator_handle_transfer_write`, and all the device will be
affected. This should be very easy to implement (maybe it already
works without modification?), but very limited / make userspace stuff
very ugly => Not ideal at all
- Creating a whole architecture to attach eBPF programs in vkms: VKMS
manage the list of attached eBPF programs, call them when it needs...
This is probably the "most flexible" option (in the sense "VKMS can do
whatever we need to fit our usecase"). This seems not trivial to
implement (drm complexity + MST complexity + BPF complexity in the same
driver seems a bad idea, espicially because VKMS don't have a lot of
maintainance and I don't feel confident introducing more complexity)
=> Can work, require some work, but may bring more complexity in VKMS
- Using BPF struct_ops: I can "simply" create/complete a struct ops for
diverse mst callbacks (see the proposition bellow). I looked at some
example, this seems to be "easy" to do, and the work in VKMS should be
limited.
=> Can work, a bit less flexible than the previous solution, but avoid
a lot of complexity
I don't know if I will be able to finish the implementation but I imagine
something like that may be a nice interface (may be not possible "as is"):
// vkms_mst.c struct_ops that can be filled by userspace with custom
// functions
// Known limitation: maximum 64 functions in this table
struct vkms_mst_ops {
// Completly overide the transfer function, so the userspace can
// do whatever he wants (even emulating a complex MST tree
// without using stuff in vkms)
handle_transfer(drm_dp_aux_msg);
// If default transfer function is used, those are the callback
// you can use (again, they will come with default
// implementation)
clear_payload_id_table(..);
link_address(..);
enum_path_ressources(..);
[...]
// Used to identify this kind of device, in my example the
// userspace could register "LG_screen", "dell dock", "HP screen",
// and then configure each mst device with the correct kind
name = "<unique name for this device kind>",
// If you want to use the default "hub" implementation, but only
// tweak one specific behavior, you can use this
base = "<name of the other structops>"
}
// Needed to implement eBPF struct_ops, called when userspace registers a
// struct_ops of type vkms_mst_ops
void register_struct_ops(new_ops...) {
vkms_registered_ops.append(new_ops).
}
// In vkms_connector.c
// Callback called by drm core to do transfer on the connector
void vkms_mst_transfer(aux, msg) {
mst_emulator = get_mst_emulator(aux);
ops = vkms_registered_ops.search_for(mst_emulator.name);
ops->handle_transfer(msg);
}
// mst_ebpf.c In the BPF program, userspace side
void handle_transfer(...) {
[...]
}
struct vkms_mst_ops {
handle_transfer = handle_transfer;
name = "lg-screen";
base = "default-screen"
}
How to use it (screen directly connected to connector, or complete
emulation by the eBPF program):
gcc mst_ebpf.c
bpftool register structops mst_ebpf
# Create vkms device + connector
mkdir -p /configfs/vkms/mydev/connectors/connector1
#[skipped initialization of plane/crtc...]
mkdir -p /configfs/vkms/mydev/mst_devices/device1
echo "lg-screen" > /configfs/vkms/mydev/mst_devices/device1/name
ln -s /configfs/vkms/mydev/connectors/connector1/device /configfs/vkms/mydev/mst_devices/device1
How to use it (hub + two screens, using vkms scaffolding to make the
emulation easier) (the idea is to do something like the tcp_congestion
algorithm, where a few of them are implemented in the kernel, but
userspace can inject custom implementations):
bpftool register mst_ebpf_screen1 # struct_ops with name=lg-screen
bpftool register mst_ebpf_screen2 # struct_ops with name=hp-screen
#[skiped initialization of vkms dev]
mkdir -p /configfs/vkms/mydev/mst_devices/screen1
mkdir -p /configfs/vkms/mydev/mst_devices/screen2
mkdir -p /configfs/vkms/mydev/mst_devices/hub
echo "lg-screen" > /configfs/vkms/mydev/mst_devices/screen1/name
echo "hp-screen" > /configfs/vkms/mydev/mst_devices/screen2/name
# default-hub is the default hub emulation provided by VKMS
echo "default-hub" > configfs/vkms/mydev/mst_devices/hub/name
ln -s /configfs/vkms/mydev/connectors/connector1/device /configfs/vkms/mydev/mst_devices/hub
ln -s /configfs/vkms/mydev/mst_devices/hub/child1 /configfs/vkms/mydev/mst_devices/screen1
ln -s /configfs/vkms/mydev/mst_devices/hub/child2 /configfs/vkms/mydev/mst_devices/screen2
A few things that this approach can bring:
- Full emulation by userspace (just add one device and provide an eBPF
program that emulates the whole MST chain)
- Partial emulation of devices (e.g., the default-screen implementation is
sufficient, but you want to tweak something inside)
- Full emulation by the kernel, using default implementations (I think
only hub and screen, just to be able to emulate the "basic"
configurations)
- And cool new to reduce the "it should be perfect from the start", if we
use kfunc + struct_ops, both can change a little bit over time (kfunc
are not part of uAPI and struct_ops allows to add argument/functions).
Stabilisation can come later.
What do you think about this idea ?
My current plan is to continue on the "kernel-only" implementation, so I
can have a working poc, and then switch to the eBPF work after.
Thanks,
Louis Chauvet
> Maxime
More information about the dri-devel
mailing list