Hi,
I'd like to get comments on this before I polish it. This is a simple way to get something similar with vkms but it heavily reuse the code provided by virtio-gpu. Please feel free to give me any feedbacks or comments.
Thanks!
The idea here is: if we run the vm headless, we don't really need to communicate with VMM, and we even don't need any VMM support for virtio-gpu. Of course, only 2d works. But it's enough for some use case. And this looks simpler than vkms.
Signed-off-by: Lepton Wu ytht.net@gmail.com --- drivers/gpu/drm/virtio/Kconfig | 9 ++ drivers/gpu/drm/virtio/Makefile | 3 + drivers/gpu/drm/virtio/virtgpu_dummy.c | 161 +++++++++++++++++++++++++ 3 files changed, 173 insertions(+) create mode 100644 drivers/gpu/drm/virtio/virtgpu_dummy.c
diff --git a/drivers/gpu/drm/virtio/Kconfig b/drivers/gpu/drm/virtio/Kconfig index eff3047052d4..9c18aace38ed 100644 --- a/drivers/gpu/drm/virtio/Kconfig +++ b/drivers/gpu/drm/virtio/Kconfig @@ -9,3 +9,12 @@ config DRM_VIRTIO_GPU QEMU based VMMs (like KVM or Xen).
If unsure say M. + +config DRM_VIRTIO_GPU_DUMMY + tristate "Virtio dummy GPU driver" + depends on DRM_VIRTIO_GPU + help + This add a new virtio GPU device which handles the virtio ring buffers + inline so it doesn't rely on VMM to provide the virtio GPU device. + Currently it only handle VIRTIO_GPU_CMD_GET_DISPLAY_INFO which is enough + for a dummy 2D VGA device. diff --git a/drivers/gpu/drm/virtio/Makefile b/drivers/gpu/drm/virtio/Makefile index 92aa2b3d349d..26d8fee1bc41 100644 --- a/drivers/gpu/drm/virtio/Makefile +++ b/drivers/gpu/drm/virtio/Makefile @@ -8,4 +8,7 @@ virtio-gpu-y := virtgpu_drv.o virtgpu_kms.o virtgpu_gem.o \ virtgpu_fence.o virtgpu_object.o virtgpu_debugfs.o virtgpu_plane.o \ virtgpu_ioctl.o virtgpu_prime.o virtgpu_trace_points.o
+virtio-gpu-dummy-y := virtgpu_dummy.o + obj-$(CONFIG_DRM_VIRTIO_GPU) += virtio-gpu.o +obj-$(CONFIG_DRM_VIRTIO_GPU_DUMMY) += virtio-gpu-dummy.o diff --git a/drivers/gpu/drm/virtio/virtgpu_dummy.c b/drivers/gpu/drm/virtio/virtgpu_dummy.c new file mode 100644 index 000000000000..8c2eb6fea47c --- /dev/null +++ b/drivers/gpu/drm/virtio/virtgpu_dummy.c @@ -0,0 +1,161 @@ +#include <linux/module.h> +#include <linux/virtio.h> +#include <linux/virtio_ids.h> +#include <linux/virtio_config.h> +#include <linux/virtio_ring.h> +#include <linux/virtio_gpu.h> + +#include "virtgpu_drv.h" + +static int virtgpu_dummy_width = 1024; +static int virtgpu_dummy_height = 768; + +MODULE_PARM_DESC(width, "Dummy VGA width"); +module_param_named(width, virtgpu_dummy_width, int, 0400); +MODULE_PARM_DESC(height, "Dummy VGA height"); +module_param_named(height, virtgpu_dummy_height, int, 0400); + +static struct bus_type dummy_bus = { + .name = "", +}; + +static struct dummy_gpu { + struct device *root; + struct virtio_device vdev; + unsigned char status; +} dummy; + +static u64 dummy_get_features(struct virtio_device *vdev) +{ + return 1ULL << VIRTIO_F_VERSION_1; +} + +static int dummy_finalize_features(struct virtio_device *vdev) +{ + return 0; +} + +static void dummy_get(struct virtio_device *vdev, unsigned int offset, + void *buf, unsigned len) +{ + static struct virtio_gpu_config config = { + .num_scanouts = 1, + }; + BUG_ON(offset + len > sizeof(config)); + memcpy(buf, (char *)&config + offset, len); +} + +static u8 dummy_get_status(struct virtio_device *vdev) +{ + struct dummy_gpu* gpu = container_of(vdev, struct dummy_gpu, vdev); + return gpu->status; +} + +static void dummy_set_status(struct virtio_device *vdev, u8 status) +{ + struct dummy_gpu* gpu = container_of(vdev, struct dummy_gpu, vdev); + BUG_ON(!status); + gpu->status = status; +} + +void process_cmd(struct vring_desc *desc, int idx) +{ + // FIXME, use chain to get resp buffer addr + char *buf = __va(desc[idx].addr); + struct virtio_gpu_vbuffer *vbuf = + (struct virtio_gpu_vbuffer *)(buf - sizeof(*vbuf)); + struct virtio_gpu_ctrl_hdr *cmd_p = (struct virtio_gpu_ctrl_hdr *)buf; + struct virtio_gpu_resp_display_info *resp; + BUG_ON(vbuf->buf != buf); + if (cmd_p->type != cpu_to_le32(VIRTIO_GPU_CMD_GET_DISPLAY_INFO)) + return; + BUG_ON(vbuf->resp_size != sizeof(struct virtio_gpu_resp_display_info)); + resp = (struct virtio_gpu_resp_display_info *)vbuf->resp_buf; + resp->pmodes[0].r.width = virtgpu_dummy_width; + resp->pmodes[0].r.height = virtgpu_dummy_height; + resp->pmodes[0].enabled = 1; +} + +static bool dummy_notify(struct virtqueue *vq) +{ + struct vring *r = (struct vring *)(vq + 1); + int used, avail; + // FIXME, handle multiple avail and also fix for big endian. + used = r->used->idx & (r->num - 1); + avail = (r->avail->idx - 1) & (r->num - 1); + r->used->ring[used].id = r->avail->ring[avail]; + r->used->idx++; + if (!strcmp(vq->name, "control")) + process_cmd(r->desc, r->avail->ring[avail]); + vq->callback(vq); + return true; +} + +static int dummy_find_vqs(struct virtio_device *vdev, unsigned nvqs, + struct virtqueue *vqs[], + vq_callback_t * callbacks[], + const char *const names[], + const bool *ctx, struct irq_affinity *desc) +{ + int i, j; + for (i = 0; i < nvqs; ++i) { + vqs[i] = vring_create_virtqueue(i, 256, SMP_CACHE_BYTES, vdev, + true, false, false, + dummy_notify, callbacks[i], + names[i]); + if (!vqs[i]) + goto err; + } + return 0; +err: + for (j = 0; j < i; ++j) { + vring_del_virtqueue(vqs[j]); + vqs[j] = NULL; + } + return -ENOMEM; +} + +static void dummy_reset(struct virtio_device *vdev) +{ +} + +static const struct virtio_config_ops dummy_vq_ops = { + .get_features = dummy_get_features, + .finalize_features = dummy_finalize_features, + .get = dummy_get, + .get_status = dummy_get_status, + .set_status = dummy_set_status, + .reset = dummy_reset, + .find_vqs = dummy_find_vqs, +}; + +static int __init virtio_gpu_dummy_init(void) +{ + int ret; + struct device * root = root_device_register("dummy"); + if (PTR_ERR_OR_ZERO(root)) + return PTR_ERR(root); + root->bus = &dummy_bus; + dummy.vdev.dev.parent = root; + dummy.vdev.id.device = VIRTIO_ID_GPU; + dummy.vdev.config = &dummy_vq_ops; + ret = register_virtio_device(&dummy.vdev); + if (ret) { + pr_err("Failed to register virtio device %d", ret); + root_device_unregister(root); + return ret; + } + dummy.root = root; + return 0; +} + +static void virtio_gpu_dummy_exit(void) +{ + if (!dummy.root) + return; + unregister_virtio_device(&dummy.vdev); + root_device_unregister(dummy.root); +} + +module_init(virtio_gpu_dummy_init); +module_exit(virtio_gpu_dummy_exit);
On Mon, Feb 24, 2020 at 03:01:54PM -0800, Lepton Wu wrote:
Hi,
I'd like to get comments on this before I polish it. This is a simple way to get something similar with vkms but it heavily reuse the code provided by virtio-gpu. Please feel free to give me any feedbacks or comments.
No.
First, what is wrong with vkms?
Second, if you really want something simple with the minimal set of drm features possible you can write a rather small but still self-contained driver when using all the drm helpers (shmem, simple display pipe) we have these days. Copy cirrus, strip it down: drop modesetting code, drop blit-from-shmem-to-vram code, drop pci probing. Maybe add module options for max/default video mode. Done.
What is the use case btw?
cheers, Gerd
On Tue, Feb 25, 2020 at 2:29 AM Gerd Hoffmann kraxel@redhat.com wrote:
On Mon, Feb 24, 2020 at 03:01:54PM -0800, Lepton Wu wrote:
Hi,
I'd like to get comments on this before I polish it. This is a simple way to get something similar with vkms but it heavily reuse the code provided by virtio-gpu. Please feel free to give me any feedbacks or comments.
No.
First, what is wrong with vkms?
The total lines of vkms driver is 1.2k+. I think it doesn't work along itself to provide things like mmap on prime fd? (I tried it months ago). Of course, that could be fixed, but then it will bring more code. While my "dummy virtio" code is only around 100 lines. And more, my "dummy virtio" device actually doesn't really depends on drm system so it's easier to back port to old kernel.
Second, if you really want something simple with the minimal set of drm features possible you can write a rather small but still self-contained driver when using all the drm helpers (shmem, simple display pipe) we have these days. Copy cirrus, strip it down: drop modesetting code, drop blit-from-shmem-to-vram code, drop pci probing. Maybe add module options for max/default video mode. Done.
I need features like prime export/import, mmap on prime fd etc. And I'd like the code could work on different kernel version. So if go with this ways, the actually add more maintain cost in the long term? since any changes at drm frame work could need change to it.
What is the use case btw?
We have images works well under qemu + virtio vga, we'd like to run these images on public cloud service like Google GCE directly. So I got the idea that if some how we can run virtio stack without vmm support. That actually would help and also let the same image run on other cloud services.
cheers, Gerd
No.
First, what is wrong with vkms?
The total lines of vkms driver is 1.2k+.
Which is small for a drm driver.
I think it doesn't work along itself to provide things like mmap on prime fd? (I tried it months ago).
Seems vkms only supports prime import, not prime export. Maybe vgem works for you?
And more, my "dummy virtio" device actually doesn't really depends on drm system so it's easier to back port to old kernel.
It depends on the virtio driver, which in turn *does* depend on drm system. So you have a indirect instead of a direct dependency. I still don't see the point ...
Second, if you really want something simple with the minimal set of drm features possible you can write a rather small but still self-contained driver when using all the drm helpers (shmem, simple display pipe) we have these days. Copy cirrus, strip it down: drop modesetting code, drop blit-from-shmem-to-vram code, drop pci probing. Maybe add module options for max/default video mode. Done.
I need features like prime export/import, mmap on prime fd etc.
The shmem helpers support that just fine.
And I'd like the code could work on different kernel version. So if go with this ways, the actually add more maintain cost in the long term?
Depends on which kernel version you need. Backporting to 5.4-lts should be easy, 4.4-lts lacks alot of the drm helpers though.
What is the use case btw?
We have images works well under qemu + virtio vga, we'd like to run these images on public cloud service like Google GCE directly.
What do these images need a drm device for? It seems not to be hardware-accelerated rendering. It also seems not to be a virtual display.
cheers, Gerd
dri-devel@lists.freedesktop.org