vhost_user: Add GpuBackend::update_scanout

Add a method and related struct to send VHOST_USER_GPU_UPDATE.

The data part of the message is not part of the struct like sugested by
the spec but a separate argument to update_scanout. This is necessary
because of limitations of having an unsized array inside of struct in Rust.
But this aproach seems preferable anyway, because it allows the consumer of
the crate to store the array in a diferent location than the struct.

Signed-off-by: Dorinda Bassey <dbassey@redhat.com>
Signed-off-by: Matej Hrica <mhrica@redhat.com>
This commit is contained in:
Matej Hrica 2024-04-16 16:22:53 +02:00 committed by Sergio López
parent d35c657c05
commit e385837436
2 changed files with 82 additions and 0 deletions

View file

@ -63,6 +63,23 @@ impl BackendInternal {
Ok(hdr)
}
fn send_message_with_payload<T: ByteValued>(
&mut self,
request: GpuBackendReq,
body: &T,
data: &[u8],
fds: Option<&[RawFd]>,
) -> io::Result<VhostUserGpuMsgHeader<GpuBackendReq>> {
self.check_state()?;
let len = mem::size_of::<T>() + data.len();
let hdr = VhostUserGpuMsgHeader::new(request, 0, len as u32);
self.sock
.send_message_with_payload(&hdr, body, data, fds)
.map_err(io_err_convert_fn("send_message_with_payload"))?;
Ok(hdr)
}
// Note that there is no VHOST_USER_PROTOCOL_F_REPLY_ACK for this protocol, some messages always
// expect a reply/ack and others don't expect a reply/ack at all.
fn recv_reply<V: ByteValued + Sized + Default + VhostUserMsgValidator>(
@ -132,6 +149,16 @@ impl GpuBackend {
Ok(())
}
/// Sends the VHOST_USER_GPU_UPDATE message to the frontend. Doesn't wait for a reply.
/// Updates the scanout content. The data payload contains the graphical bits.
/// The display should be flushed and presented.
pub fn update_scanout(&self, update: &VhostUserGpuUpdate, data: &[u8]) -> io::Result<()> {
let mut node = self.node();
node.send_message_with_payload(GpuBackendReq::UPDATE, update, data, None)?;
Ok(())
}
/// Create a new instance from a `UnixStream` object.
pub fn from_stream(sock: UnixStream) -> Self {
Self::new(Endpoint::<VhostUserGpuMsgHeader<GpuBackendReq>>::from_stream(sock))
@ -274,4 +301,38 @@ mod tests {
sender_thread.join().expect("Failed to send!");
}
#[test]
fn test_update_scanout() {
let (mut frontend, backend) = frontend_backend_pair();
let request = VhostUserGpuUpdate {
scanout_id: 1,
x: 30,
y: 40,
width: 10,
height: 10,
};
let payload = [1u8; 4 * 10 * 10];
let sender_thread = thread::spawn(move || {
let _: () = backend.update_scanout(&request, &payload).unwrap();
});
let mut recv_buf = [0u8; 4096];
let (hdr, req_body, recv_buf_len, fds) = frontend
.recv_payload_into_buf::<VhostUserGpuUpdate>(&mut recv_buf)
.unwrap();
assert!(fds.is_none());
assert_hdr(
&hdr,
GpuBackendReq::UPDATE,
size_of_val(&request) + payload.len(),
);
assert_eq!(req_body, request);
assert_eq!(&payload[..], &recv_buf[..recv_buf_len]);
sender_thread.join().expect("Failed to send!");
}
}

View file

@ -262,6 +262,27 @@ unsafe impl ByteValued for VhostUserGpuEdidRequest {}
impl VhostUserMsgValidator for VhostUserGpuEdidRequest {}
/// The VhostUserGpuUpdate from the vhost-user-gpu specification.
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
#[repr(C)]
pub struct VhostUserGpuUpdate {
/// The id of the scanout that is being updated
pub scanout_id: u32,
/// The x coordinate of the region to update
pub x: u32,
/// The y coordinate of the region to update
pub y: u32,
/// The width of the region to update
pub width: u32,
/// The height of the region to update
pub height: u32,
}
// SAFETY: Safe because all fields are POD.
unsafe impl ByteValued for VhostUserGpuUpdate {}
impl VhostUserMsgValidator for VhostUserGpuUpdate {}
/// The virtio_gpu_resp_edid struct from the virtio specification.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[repr(C)]