Re: [PATCH] drm/virtio: Add window server support

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

 



On 28 December 2017 at 12:53, Tomeu Vizoso <tomeu.vizoso@xxxxxxxxxxxxx> wrote:
> This is to allow clients running within VMs to be able to communicate
> with a compositor in the host. Clients will use the communication
> protocol that the compositor supports, and virtio-gpu will assist with
> making buffers available in both sides, and copying content as needed.

Here is the qemu side, a bit hackier atm:

https://gitlab.collabora.com/tomeu/qemu/commits/winsrv-wip

Regards,

Tomeu

> It is expected that a service in the guest will act as a proxy,
> interacting with virtio-gpu to support unmodified clients. For some
> features of the protocol, the hypervisor might have to intervene and
> also parse the protocol data to properly bridge resources. The following
> IOCTLs have been added to this effect:
>
> *_WINSRV_CONNECT: Opens a connection to the compositor in the host,
> returns a FD that represents this connection and on which the following
> IOCTLs can be used. Callers are expected to poll this FD for new
> messages from the compositor.
>
> *_WINSRV_TX: Asks the hypervisor to forward a message to the compositor
>
> *_WINSRV_RX: Returns all queued messages
>
> Alongside protocol data that is opaque to the kernel, the client can
> send file descriptors that reference GEM buffers allocated by
> virtio-gpu. The guest proxy is expected to figure out when a client is
> passing a FD that refers to shared memory in the guest and allocate a
> virtio-gpu buffer of the same size with DRM_VIRTGPU_RESOURCE_CREATE.
>
> When the client notifies the compositor that it can read from that buffer,
> the proxy should copy the contents from the SHM region to the virtio-gpu
> resource and call DRM_VIRTGPU_TRANSFER_TO_HOST.
>
> This has been tested with Wayland clients that make use of wl_shm to
> pass buffers to the compositor, but is expected to work similarly for X
> clients that make use of MIT-SHM with FD passing.
>
> v2: * Add padding to two virtio command structs
>     * Properly cast two __user pointers (kbuild test robot)
>
> Signed-off-by: Tomeu Vizoso <tomeu.vizoso@xxxxxxxxxxxxx>
> Cc: Zach Reizner <zachr@xxxxxxxxxx>
>
> ---
>
> Hi,
>
> this work is based on the virtio_wl driver in the ChromeOS kernel by
> Zach Reizner, currently at:
>
> https://chromium.googlesource.com/chromiumos/third_party/kernel/+/chromeos-4.4/drivers/virtio/virtio_wl.c
>
> There's two features missing in this patch when compared with virtio_wl:
>
> * Allow the guest access directly host memory, without having to resort
> to TRANSFER_TO_HOST
>
> * Pass FDs from host to guest (Wayland specifies that the compositor
> shares keyboard data with the guest via a shared buffer)
>
> I plan to work on this next, but I would like to get some comments on
> the general approach so I can better choose which patch to follow.
>
> Thanks,
>
> Tomeu
> ---
>  drivers/gpu/drm/virtio/virtgpu_drv.h   |  39 ++++-
>  drivers/gpu/drm/virtio/virtgpu_ioctl.c | 168 +++++++++++++++++++
>  drivers/gpu/drm/virtio/virtgpu_kms.c   |  58 +++++--
>  drivers/gpu/drm/virtio/virtgpu_vq.c    | 285 ++++++++++++++++++++++++++++++++-
>  include/uapi/drm/virtgpu_drm.h         |  29 ++++
>  include/uapi/linux/virtio_gpu.h        |  41 +++++
>  6 files changed, 605 insertions(+), 15 deletions(-)
>
> diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h
> index da2fb585fea4..268b386e1232 100644
> --- a/drivers/gpu/drm/virtio/virtgpu_drv.h
> +++ b/drivers/gpu/drm/virtio/virtgpu_drv.h
> @@ -178,6 +178,8 @@ struct virtio_gpu_device {
>
>         struct virtio_gpu_queue ctrlq;
>         struct virtio_gpu_queue cursorq;
> +       struct virtio_gpu_queue winsrv_rxq;
> +       struct virtio_gpu_queue winsrv_txq;
>         struct kmem_cache *vbufs;
>         bool vqs_ready;
>
> @@ -205,10 +207,32 @@ struct virtio_gpu_device {
>
>  struct virtio_gpu_fpriv {
>         uint32_t ctx_id;
> +
> +       struct list_head winsrv_conns; /* list of virtio_gpu_winsrv_conn */
> +       spinlock_t winsrv_lock;
> +};
> +
> +struct virtio_gpu_winsrv_rx_qentry {
> +       struct virtio_gpu_winsrv_rx *cmd;
> +       struct list_head next;
> +};
> +
> +struct virtio_gpu_winsrv_conn {
> +       struct virtio_gpu_device *vgdev;
> +
> +       spinlock_t lock;
> +
> +       int fd;
> +       struct drm_file *drm_file;
> +
> +       struct list_head cmdq; /* queue of virtio_gpu_winsrv_rx_qentry */
> +       wait_queue_head_t cmdwq;
> +
> +       struct list_head next;
>  };
>
>  /* virtio_ioctl.c */
> -#define DRM_VIRTIO_NUM_IOCTLS 10
> +#define DRM_VIRTIO_NUM_IOCTLS 11
>  extern struct drm_ioctl_desc virtio_gpu_ioctls[DRM_VIRTIO_NUM_IOCTLS];
>
>  /* virtio_kms.c */
> @@ -318,9 +342,22 @@ virtio_gpu_cmd_resource_create_3d(struct virtio_gpu_device *vgdev,
>  void virtio_gpu_ctrl_ack(struct virtqueue *vq);
>  void virtio_gpu_cursor_ack(struct virtqueue *vq);
>  void virtio_gpu_fence_ack(struct virtqueue *vq);
> +void virtio_gpu_winsrv_tx_ack(struct virtqueue *vq);
> +void virtio_gpu_winsrv_rx_read(struct virtqueue *vq);
>  void virtio_gpu_dequeue_ctrl_func(struct work_struct *work);
>  void virtio_gpu_dequeue_cursor_func(struct work_struct *work);
> +void virtio_gpu_dequeue_winsrv_rx_func(struct work_struct *work);
> +void virtio_gpu_dequeue_winsrv_tx_func(struct work_struct *work);
>  void virtio_gpu_dequeue_fence_func(struct work_struct *work);
> +void virtio_gpu_fill_winsrv_rx(struct virtio_gpu_device *vgdev);
> +void virtio_gpu_queue_winsrv_rx_in(struct virtio_gpu_device *vgdev,
> +                                  struct virtio_gpu_winsrv_rx *cmd);
> +int virtio_gpu_cmd_winsrv_connect(struct virtio_gpu_device *vgdev, int fd);
> +void virtio_gpu_cmd_winsrv_disconnect(struct virtio_gpu_device *vgdev, int fd);
> +int virtio_gpu_cmd_winsrv_tx(struct virtio_gpu_device *vgdev,
> +                            const char __user *buffer, u32 len,
> +                            int *fds, struct virtio_gpu_winsrv_conn *conn,
> +                            bool nonblock);
>
>  /* virtio_gpu_display.c */
>  int virtio_gpu_framebuffer_init(struct drm_device *dev,
> diff --git a/drivers/gpu/drm/virtio/virtgpu_ioctl.c b/drivers/gpu/drm/virtio/virtgpu_ioctl.c
> index 0528edb4a2bf..630ed16d5f74 100644
> --- a/drivers/gpu/drm/virtio/virtgpu_ioctl.c
> +++ b/drivers/gpu/drm/virtio/virtgpu_ioctl.c
> @@ -25,6 +25,9 @@
>   * OTHER DEALINGS IN THE SOFTWARE.
>   */
>
> +#include <linux/anon_inodes.h>
> +#include <linux/syscalls.h>
> +
>  #include <drm/drmP.h>
>  #include <drm/virtgpu_drm.h>
>  #include <drm/ttm/ttm_execbuf_util.h>
> @@ -527,6 +530,168 @@ static int virtio_gpu_get_caps_ioctl(struct drm_device *dev,
>         return 0;
>  }
>
> +static unsigned int winsrv_poll(struct file *filp,
> +                               struct poll_table_struct *wait)
> +{
> +       struct virtio_gpu_winsrv_conn *conn = filp->private_data;
> +       unsigned int mask = 0;
> +
> +       spin_lock(&conn->lock);
> +       poll_wait(filp, &conn->cmdwq, wait);
> +       if (!list_empty(&conn->cmdq))
> +               mask |= POLLIN | POLLRDNORM;
> +       spin_unlock(&conn->lock);
> +
> +       return mask;
> +}
> +
> +static int winsrv_ioctl_rx(struct virtio_gpu_device *vgdev,
> +                          struct virtio_gpu_winsrv_conn *conn,
> +                          struct drm_virtgpu_winsrv *cmd)
> +{
> +       struct virtio_gpu_winsrv_rx_qentry *qentry, *tmp;
> +       struct virtio_gpu_winsrv_rx *virtio_cmd;
> +       int available_len = cmd->len;
> +       int read_count = 0;
> +
> +       list_for_each_entry_safe(qentry, tmp, &conn->cmdq, next) {
> +               virtio_cmd = qentry->cmd;
> +               if (virtio_cmd->len > available_len)
> +                       return 0;
> +
> +               if (copy_to_user((void __user *)cmd->data + read_count,
> +                                virtio_cmd->data,
> +                                virtio_cmd->len)) {
> +                       /* return error unless we have some data to return */
> +                       if (read_count == 0)
> +                               return -EFAULT;
> +               }
> +
> +               /*
> +                * here we could export resource IDs to FDs, but no protocol
> +                * as of today requires it
> +                */
> +
> +               available_len -= virtio_cmd->len;
> +               read_count += virtio_cmd->len;
> +
> +               virtio_gpu_queue_winsrv_rx_in(vgdev, virtio_cmd);
> +
> +               list_del(&qentry->next);
> +               kfree(qentry);
> +       }
> +
> +       cmd->len = read_count;
> +
> +       return 0;
> +}
> +
> +static long winsrv_ioctl(struct file *filp, unsigned int cmd,
> +                        unsigned long arg)
> +{
> +       struct virtio_gpu_winsrv_conn *conn = filp->private_data;
> +       struct virtio_gpu_device *vgdev = conn->vgdev;
> +       struct drm_virtgpu_winsrv winsrv_cmd;
> +       int ret;
> +
> +       if (_IOC_SIZE(cmd) > sizeof(winsrv_cmd))
> +               return -EINVAL;
> +
> +       if (copy_from_user(&winsrv_cmd, (void __user *)arg,
> +                          _IOC_SIZE(cmd)) != 0)
> +               return -EFAULT;
> +
> +       switch (cmd) {
> +       case DRM_IOCTL_VIRTGPU_WINSRV_RX:
> +               ret = winsrv_ioctl_rx(vgdev, conn, &winsrv_cmd);
> +               if (copy_to_user((void __user *)arg, &winsrv_cmd,
> +                                _IOC_SIZE(cmd)) != 0)
> +                       return -EFAULT;
> +
> +               break;
> +
> +       case DRM_IOCTL_VIRTGPU_WINSRV_TX:
> +               ret = virtio_gpu_cmd_winsrv_tx(vgdev,
> +                               u64_to_user_ptr(winsrv_cmd.data),
> +                               winsrv_cmd.len,
> +                               winsrv_cmd.fds,
> +                               conn,
> +                               filp->f_flags & O_NONBLOCK);
> +               break;
> +       default:
> +               ret = -EINVAL;
> +       }
> +
> +       return ret;
> +}
> +
> +static int winsrv_release(struct inode *inodep, struct file *filp)
> +{
> +       struct virtio_gpu_winsrv_conn *conn = filp->private_data;
> +       struct virtio_gpu_device *vgdev = conn->vgdev;
> +
> +       virtio_gpu_cmd_winsrv_disconnect(vgdev, conn->fd);
> +
> +       list_del(&conn->next);
> +       kfree(conn);
> +
> +       return 0;
> +}
> +
> +static const struct file_operations winsrv_fops = {
> +
> +       .poll = winsrv_poll,
> +       .unlocked_ioctl = winsrv_ioctl,
> +       .release = winsrv_release,
> +};
> +
> +static int virtio_gpu_winsrv_connect(struct drm_device *dev, void *data,
> +                                    struct drm_file *file)
> +{
> +       struct virtio_gpu_device *vgdev = dev->dev_private;
> +       struct virtio_gpu_fpriv *vfpriv = file->driver_priv;
> +       struct drm_virtgpu_winsrv_connect *args = data;
> +       struct virtio_gpu_winsrv_conn *conn;
> +       int ret;
> +
> +       conn = kzalloc(sizeof(*conn), GFP_KERNEL);
> +       if (!conn)
> +               return -ENOMEM;
> +
> +       conn->vgdev = vgdev;
> +       conn->drm_file = file;
> +       spin_lock_init(&conn->lock);
> +       INIT_LIST_HEAD(&conn->cmdq);
> +       init_waitqueue_head(&conn->cmdwq);
> +
> +       ret = anon_inode_getfd("[virtgpu_winsrv]", &winsrv_fops, conn,
> +                              O_CLOEXEC | O_RDWR);
> +       if (ret < 0)
> +               goto free_conn;
> +
> +       conn->fd = ret;
> +
> +       ret = virtio_gpu_cmd_winsrv_connect(vgdev, conn->fd);
> +       if (ret < 0)
> +               goto close_fd;
> +
> +       spin_lock(&vfpriv->winsrv_lock);
> +       list_add_tail(&conn->next, &vfpriv->winsrv_conns);
> +       spin_unlock(&vfpriv->winsrv_lock);
> +
> +       args->fd = conn->fd;
> +
> +       return 0;
> +
> +close_fd:
> +       sys_close(conn->fd);
> +
> +free_conn:
> +       kfree(conn);
> +
> +       return ret;
> +}
> +
>  struct drm_ioctl_desc virtio_gpu_ioctls[DRM_VIRTIO_NUM_IOCTLS] = {
>         DRM_IOCTL_DEF_DRV(VIRTGPU_MAP, virtio_gpu_map_ioctl,
>                           DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW),
> @@ -558,4 +723,7 @@ struct drm_ioctl_desc virtio_gpu_ioctls[DRM_VIRTIO_NUM_IOCTLS] = {
>
>         DRM_IOCTL_DEF_DRV(VIRTGPU_GET_CAPS, virtio_gpu_get_caps_ioctl,
>                           DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW),
> +
> +       DRM_IOCTL_DEF_DRV(VIRTGPU_WINSRV_CONNECT, virtio_gpu_winsrv_connect,
> +                         DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW),
>  };
> diff --git a/drivers/gpu/drm/virtio/virtgpu_kms.c b/drivers/gpu/drm/virtio/virtgpu_kms.c
> index 6400506a06b0..ad7872037982 100644
> --- a/drivers/gpu/drm/virtio/virtgpu_kms.c
> +++ b/drivers/gpu/drm/virtio/virtgpu_kms.c
> @@ -128,13 +128,15 @@ static void virtio_gpu_get_capsets(struct virtio_gpu_device *vgdev,
>  int virtio_gpu_driver_load(struct drm_device *dev, unsigned long flags)
>  {
>         static vq_callback_t *callbacks[] = {
> -               virtio_gpu_ctrl_ack, virtio_gpu_cursor_ack
> +               virtio_gpu_ctrl_ack, virtio_gpu_cursor_ack,
> +               virtio_gpu_winsrv_rx_read, virtio_gpu_winsrv_tx_ack
>         };
> -       static const char * const names[] = { "control", "cursor" };
> +       static const char * const names[] = { "control", "cursor",
> +                                             "winsrv-rx", "winsrv-tx" };
>
>         struct virtio_gpu_device *vgdev;
>         /* this will expand later */
> -       struct virtqueue *vqs[2];
> +       struct virtqueue *vqs[4];
>         u32 num_scanouts, num_capsets;
>         int ret;
>
> @@ -158,6 +160,10 @@ int virtio_gpu_driver_load(struct drm_device *dev, unsigned long flags)
>         init_waitqueue_head(&vgdev->resp_wq);
>         virtio_gpu_init_vq(&vgdev->ctrlq, virtio_gpu_dequeue_ctrl_func);
>         virtio_gpu_init_vq(&vgdev->cursorq, virtio_gpu_dequeue_cursor_func);
> +       virtio_gpu_init_vq(&vgdev->winsrv_rxq,
> +                          virtio_gpu_dequeue_winsrv_rx_func);
> +       virtio_gpu_init_vq(&vgdev->winsrv_txq,
> +                          virtio_gpu_dequeue_winsrv_tx_func);
>
>         vgdev->fence_drv.context = dma_fence_context_alloc(1);
>         spin_lock_init(&vgdev->fence_drv.lock);
> @@ -175,13 +181,15 @@ int virtio_gpu_driver_load(struct drm_device *dev, unsigned long flags)
>         DRM_INFO("virgl 3d acceleration not supported by guest\n");
>  #endif
>
> -       ret = virtio_find_vqs(vgdev->vdev, 2, vqs, callbacks, names, NULL);
> +       ret = virtio_find_vqs(vgdev->vdev, 4, vqs, callbacks, names, NULL);
>         if (ret) {
>                 DRM_ERROR("failed to find virt queues\n");
>                 goto err_vqs;
>         }
>         vgdev->ctrlq.vq = vqs[0];
>         vgdev->cursorq.vq = vqs[1];
> +       vgdev->winsrv_rxq.vq = vqs[2];
> +       vgdev->winsrv_txq.vq = vqs[3];
>         ret = virtio_gpu_alloc_vbufs(vgdev);
>         if (ret) {
>                 DRM_ERROR("failed to alloc vbufs\n");
> @@ -215,6 +223,9 @@ int virtio_gpu_driver_load(struct drm_device *dev, unsigned long flags)
>                 goto err_modeset;
>
>         virtio_device_ready(vgdev->vdev);
> +
> +       virtio_gpu_fill_winsrv_rx(vgdev);
> +
>         vgdev->vqs_ready = true;
>
>         if (num_capsets)
> @@ -256,6 +267,8 @@ void virtio_gpu_driver_unload(struct drm_device *dev)
>         vgdev->vqs_ready = false;
>         flush_work(&vgdev->ctrlq.dequeue_work);
>         flush_work(&vgdev->cursorq.dequeue_work);
> +       flush_work(&vgdev->winsrv_rxq.dequeue_work);
> +       flush_work(&vgdev->winsrv_txq.dequeue_work);
>         flush_work(&vgdev->config_changed_work);
>         vgdev->vdev->config->del_vqs(vgdev->vdev);
>
> @@ -274,25 +287,43 @@ int virtio_gpu_driver_open(struct drm_device *dev, struct drm_file *file)
>         uint32_t id;
>         char dbgname[64], tmpname[TASK_COMM_LEN];
>
> -       /* can't create contexts without 3d renderer */
> -       if (!vgdev->has_virgl_3d)
> -               return 0;
> -
> -       get_task_comm(tmpname, current);
> -       snprintf(dbgname, sizeof(dbgname), "%s", tmpname);
> -       dbgname[63] = 0;
>         /* allocate a virt GPU context for this opener */
>         vfpriv = kzalloc(sizeof(*vfpriv), GFP_KERNEL);
>         if (!vfpriv)
>                 return -ENOMEM;
>
> -       virtio_gpu_context_create(vgdev, strlen(dbgname), dbgname, &id);
> +       /* can't create contexts without 3d renderer */
> +       if (vgdev->has_virgl_3d) {
> +               get_task_comm(tmpname, current);
> +               snprintf(dbgname, sizeof(dbgname), "%s", tmpname);
> +               dbgname[63] = 0;
> +
> +               virtio_gpu_context_create(vgdev, strlen(dbgname), dbgname, &id);
> +
> +               vfpriv->ctx_id = id;
> +       }
> +
> +       spin_lock_init(&vfpriv->winsrv_lock);
> +       INIT_LIST_HEAD(&vfpriv->winsrv_conns);
>
> -       vfpriv->ctx_id = id;
>         file->driver_priv = vfpriv;
> +
>         return 0;
>  }
>
> +static void virtio_gpu_cleanup_conns(struct virtio_gpu_fpriv *vfpriv)
> +{
> +       struct virtio_gpu_winsrv_conn *conn, *tmp;
> +       struct virtio_gpu_winsrv_rx_qentry *qentry, *tmp2;
> +
> +       list_for_each_entry_safe(conn, tmp, &vfpriv->winsrv_conns, next) {
> +               list_for_each_entry_safe(qentry, tmp2, &conn->cmdq, next) {
> +                       kfree(qentry);
> +               }
> +               kfree(conn);
> +       }
> +}
> +
>  void virtio_gpu_driver_postclose(struct drm_device *dev, struct drm_file *file)
>  {
>         struct virtio_gpu_device *vgdev = dev->dev_private;
> @@ -303,6 +334,7 @@ void virtio_gpu_driver_postclose(struct drm_device *dev, struct drm_file *file)
>
>         vfpriv = file->driver_priv;
>
> +       virtio_gpu_cleanup_conns(vfpriv);
>         virtio_gpu_context_destroy(vgdev, vfpriv->ctx_id);
>         kfree(vfpriv);
>         file->driver_priv = NULL;
> diff --git a/drivers/gpu/drm/virtio/virtgpu_vq.c b/drivers/gpu/drm/virtio/virtgpu_vq.c
> index 9eb96fb2c147..ea5f9352d364 100644
> --- a/drivers/gpu/drm/virtio/virtgpu_vq.c
> +++ b/drivers/gpu/drm/virtio/virtgpu_vq.c
> @@ -32,7 +32,7 @@
>  #include <linux/virtio_config.h>
>  #include <linux/virtio_ring.h>
>
> -#define MAX_INLINE_CMD_SIZE   96
> +#define MAX_INLINE_CMD_SIZE   144
>  #define MAX_INLINE_RESP_SIZE  24
>  #define VBUFFER_SIZE          (sizeof(struct virtio_gpu_vbuffer) \
>                                + MAX_INLINE_CMD_SIZE             \
> @@ -72,6 +72,67 @@ void virtio_gpu_cursor_ack(struct virtqueue *vq)
>         schedule_work(&vgdev->cursorq.dequeue_work);
>  }
>
> +void virtio_gpu_winsrv_rx_read(struct virtqueue *vq)
> +{
> +       struct drm_device *dev = vq->vdev->priv;
> +       struct virtio_gpu_device *vgdev = dev->dev_private;
> +
> +       schedule_work(&vgdev->winsrv_rxq.dequeue_work);
> +}
> +
> +void virtio_gpu_winsrv_tx_ack(struct virtqueue *vq)
> +{
> +       struct drm_device *dev = vq->vdev->priv;
> +       struct virtio_gpu_device *vgdev = dev->dev_private;
> +
> +       schedule_work(&vgdev->winsrv_txq.dequeue_work);
> +}
> +
> +void virtio_gpu_queue_winsrv_rx_in(struct virtio_gpu_device *vgdev,
> +                                  struct virtio_gpu_winsrv_rx *cmd)
> +{
> +       struct virtqueue *vq = vgdev->winsrv_rxq.vq;
> +       struct scatterlist sg[1];
> +       int ret;
> +
> +       sg_init_one(sg, cmd, sizeof(*cmd));
> +
> +       spin_lock(&vgdev->winsrv_rxq.qlock);
> +retry:
> +       ret = virtqueue_add_inbuf(vq, sg, 1, cmd, GFP_KERNEL);
> +       if (ret == -ENOSPC) {
> +               spin_unlock(&vgdev->winsrv_rxq.qlock);
> +               wait_event(vgdev->winsrv_rxq.ack_queue, vq->num_free);
> +               spin_lock(&vgdev->winsrv_rxq.qlock);
> +               goto retry;
> +       }
> +       virtqueue_kick(vq);
> +       spin_unlock(&vgdev->winsrv_rxq.qlock);
> +}
> +
> +void virtio_gpu_fill_winsrv_rx(struct virtio_gpu_device *vgdev)
> +{
> +       struct virtqueue *vq = vgdev->winsrv_rxq.vq;
> +       struct virtio_gpu_winsrv_rx *cmd;
> +       int ret = 0;
> +
> +       while (vq->num_free > 0) {
> +               cmd = kmalloc(sizeof(*cmd), GFP_KERNEL);
> +               if (!cmd) {
> +                       ret = -ENOMEM;
> +                       goto clear_queue;
> +               }
> +
> +               virtio_gpu_queue_winsrv_rx_in(vgdev, cmd);
> +       }
> +
> +       return;
> +
> +clear_queue:
> +       while ((cmd = virtqueue_detach_unused_buf(vq)))
> +               kfree(cmd);
> +}
> +
>  int virtio_gpu_alloc_vbufs(struct virtio_gpu_device *vgdev)
>  {
>         vgdev->vbufs = kmem_cache_create("virtio-gpu-vbufs",
> @@ -258,6 +319,96 @@ void virtio_gpu_dequeue_cursor_func(struct work_struct *work)
>         wake_up(&vgdev->cursorq.ack_queue);
>  }
>
> +void virtio_gpu_dequeue_winsrv_tx_func(struct work_struct *work)
> +{
> +       struct virtio_gpu_device *vgdev =
> +               container_of(work, struct virtio_gpu_device,
> +                            winsrv_txq.dequeue_work);
> +       struct virtio_gpu_vbuffer *vbuf;
> +       int len;
> +
> +       spin_lock(&vgdev->winsrv_txq.qlock);
> +       do {
> +               while ((vbuf = virtqueue_get_buf(vgdev->winsrv_txq.vq, &len)))
> +                       free_vbuf(vgdev, vbuf);
> +       } while (!virtqueue_enable_cb(vgdev->winsrv_txq.vq));
> +       spin_unlock(&vgdev->winsrv_txq.qlock);
> +
> +       wake_up(&vgdev->winsrv_txq.ack_queue);
> +}
> +
> +static struct virtio_gpu_winsrv_conn *find_conn(struct virtio_gpu_device *vgdev,
> +                                               int fd)
> +{
> +       struct virtio_gpu_winsrv_conn *conn;
> +       struct drm_device *ddev = vgdev->ddev;
> +       struct drm_file *file;
> +       struct virtio_gpu_fpriv *vfpriv;
> +
> +       mutex_lock(&ddev->filelist_mutex);
> +       list_for_each_entry(file, &ddev->filelist, lhead) {
> +               vfpriv = file->driver_priv;
> +               spin_lock(&vfpriv->winsrv_lock);
> +               list_for_each_entry(conn, &vfpriv->winsrv_conns, next) {
> +                       if (conn->fd == fd) {
> +                               spin_lock(&conn->lock);
> +                               spin_unlock(&vfpriv->winsrv_lock);
> +                               mutex_unlock(&ddev->filelist_mutex);
> +                               return conn;
> +                       }
> +               }
> +               spin_unlock(&vfpriv->winsrv_lock);
> +       }
> +       mutex_unlock(&ddev->filelist_mutex);
> +
> +       return NULL;
> +}
> +
> +static void handle_rx_cmd(struct virtio_gpu_device *vgdev,
> +                         struct virtio_gpu_winsrv_rx *cmd)
> +{
> +       struct virtio_gpu_winsrv_conn *conn;
> +       struct virtio_gpu_winsrv_rx_qentry *qentry;
> +
> +       conn = find_conn(vgdev, cmd->client_fd);
> +       if (!conn) {
> +               DRM_DEBUG("recv for unknown client fd %u\n", cmd->client_fd);
> +               return;
> +       }
> +
> +       qentry = kzalloc(sizeof(*qentry), GFP_KERNEL);
> +       if (!qentry) {
> +               spin_unlock(&conn->lock);
> +               DRM_DEBUG("failed to allocate qentry for winsrv connection\n");
> +               return;
> +       }
> +
> +       qentry->cmd = cmd;
> +
> +       list_add_tail(&qentry->next, &conn->cmdq);
> +       wake_up_interruptible(&conn->cmdwq);
> +       spin_unlock(&conn->lock);
> +}
> +
> +void virtio_gpu_dequeue_winsrv_rx_func(struct work_struct *work)
> +{
> +       struct virtio_gpu_device *vgdev =
> +               container_of(work, struct virtio_gpu_device,
> +                            winsrv_rxq.dequeue_work);
> +       struct virtio_gpu_winsrv_rx *cmd;
> +       unsigned int len;
> +
> +       spin_lock(&vgdev->winsrv_rxq.qlock);
> +       while ((cmd = virtqueue_get_buf(vgdev->winsrv_rxq.vq, &len)) != NULL) {
> +               spin_unlock(&vgdev->winsrv_rxq.qlock);
> +               handle_rx_cmd(vgdev, cmd);
> +               spin_lock(&vgdev->winsrv_rxq.qlock);
> +       }
> +       spin_unlock(&vgdev->winsrv_rxq.qlock);
> +
> +       virtqueue_kick(vgdev->winsrv_rxq.vq);
> +}
> +
>  static int virtio_gpu_queue_ctrl_buffer_locked(struct virtio_gpu_device *vgdev,
>                                                struct virtio_gpu_vbuffer *vbuf)
>                 __releases(&vgdev->ctrlq.qlock)
> @@ -380,6 +531,41 @@ static int virtio_gpu_queue_cursor(struct virtio_gpu_device *vgdev,
>         return ret;
>  }
>
> +static int virtio_gpu_queue_winsrv_tx(struct virtio_gpu_device *vgdev,
> +                                     struct virtio_gpu_vbuffer *vbuf)
> +{
> +       struct virtqueue *vq = vgdev->winsrv_txq.vq;
> +       struct scatterlist *sgs[2], vcmd, vout;
> +       int ret;
> +
> +       if (!vgdev->vqs_ready)
> +               return -ENODEV;
> +
> +       sg_init_one(&vcmd, vbuf->buf, vbuf->size);
> +       sgs[0] = &vcmd;
> +
> +       sg_init_one(&vout, vbuf->data_buf, vbuf->data_size);
> +       sgs[1] = &vout;
> +
> +       spin_lock(&vgdev->winsrv_txq.qlock);
> +retry:
> +       ret = virtqueue_add_sgs(vq, sgs, 2, 0, vbuf, GFP_ATOMIC);
> +       if (ret == -ENOSPC) {
> +               spin_unlock(&vgdev->winsrv_txq.qlock);
> +               wait_event(vgdev->winsrv_txq.ack_queue, vq->num_free);
> +               spin_lock(&vgdev->winsrv_txq.qlock);
> +               goto retry;
> +       }
> +
> +       virtqueue_kick(vq);
> +
> +       spin_unlock(&vgdev->winsrv_txq.qlock);
> +
> +       if (!ret)
> +               ret = vq->num_free;
> +       return ret;
> +}
> +
>  /* just create gem objects for userspace and long lived objects,
>     just use dma_alloced pages for the queue objects? */
>
> @@ -890,3 +1076,100 @@ void virtio_gpu_cursor_ping(struct virtio_gpu_device *vgdev,
>         memcpy(cur_p, &output->cursor, sizeof(output->cursor));
>         virtio_gpu_queue_cursor(vgdev, vbuf);
>  }
> +
> +int virtio_gpu_cmd_winsrv_connect(struct virtio_gpu_device *vgdev, int fd)
> +{
> +       struct virtio_gpu_winsrv_connect *cmd_p;
> +       struct virtio_gpu_vbuffer *vbuf;
> +
> +       cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p));
> +       memset(cmd_p, 0, sizeof(*cmd_p));
> +
> +       cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_WINSRV_CONNECT);
> +       cmd_p->client_fd = cpu_to_le32(fd);
> +       return virtio_gpu_queue_ctrl_buffer(vgdev, vbuf);
> +}
> +
> +void virtio_gpu_cmd_winsrv_disconnect(struct virtio_gpu_device *vgdev, int fd)
> +{
> +       struct virtio_gpu_winsrv_disconnect *cmd_p;
> +       struct virtio_gpu_vbuffer *vbuf;
> +
> +       cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p));
> +       memset(cmd_p, 0, sizeof(*cmd_p));
> +
> +       cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_WINSRV_DISCONNECT);
> +       cmd_p->client_fd = cpu_to_le32(fd);
> +       virtio_gpu_queue_ctrl_buffer(vgdev, vbuf);
> +}
> +
> +int virtio_gpu_cmd_winsrv_tx(struct virtio_gpu_device *vgdev,
> +                            const char __user *buffer, u32 len,
> +                            int *fds, struct virtio_gpu_winsrv_conn *conn,
> +                            bool nonblock)
> +{
> +       int client_fd = conn->fd;
> +       struct drm_file *file = conn->drm_file;
> +       struct virtio_gpu_winsrv_tx *cmd_p;
> +       struct virtio_gpu_vbuffer *vbuf;
> +       uint32_t gem_handle;
> +       struct drm_gem_object *gobj = NULL;
> +       struct virtio_gpu_object *qobj = NULL;
> +       int ret, i, fd;
> +
> +       cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p));
> +       memset(cmd_p, 0, sizeof(*cmd_p));
> +
> +       cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_WINSRV_TX);
> +
> +       for (i = 0; i < VIRTIO_GPU_WINSRV_MAX_ALLOCS; i++) {
> +               cmd_p->resource_ids[i] = -1;
> +
> +               fd = fds[i];
> +               if (fd < 0)
> +                       break;
> +
> +               ret = drm_gem_prime_fd_to_handle(vgdev->ddev, file, fd,
> +                                                &gem_handle);
> +               if (ret != 0)
> +                       goto err_free_vbuf;
> +
> +               gobj = drm_gem_object_lookup(file, gem_handle);
> +               if (gobj == NULL) {
> +                       ret = -ENOENT;
> +                       goto err_free_vbuf;
> +               }
> +
> +               qobj = gem_to_virtio_gpu_obj(gobj);
> +               cmd_p->resource_ids[i] = qobj->hw_res_handle;
> +       }
> +
> +       cmd_p->client_fd = client_fd;
> +       cmd_p->len = cpu_to_le32(len);
> +
> +       /* gets freed when the ring has consumed it */
> +       vbuf->data_buf = kmalloc(cmd_p->len, GFP_KERNEL);
> +       if (!vbuf->data_buf) {
> +               DRM_ERROR("failed to allocate winsrv tx buffer\n");
> +               ret = -ENOMEM;
> +               goto err_free_vbuf;
> +       }
> +
> +       vbuf->data_size = cmd_p->len;
> +
> +       if (copy_from_user(vbuf->data_buf, buffer, cmd_p->len)) {
> +               ret = -EFAULT;
> +               goto err_free_databuf;
> +       }
> +
> +       virtio_gpu_queue_winsrv_tx(vgdev, vbuf);
> +
> +       return 0;
> +
> +err_free_databuf:
> +       kfree(vbuf->data_buf);
> +err_free_vbuf:
> +       free_vbuf(vgdev, vbuf);
> +
> +       return ret;
> +}
> diff --git a/include/uapi/drm/virtgpu_drm.h b/include/uapi/drm/virtgpu_drm.h
> index 91a31ffed828..89b0a1a707a7 100644
> --- a/include/uapi/drm/virtgpu_drm.h
> +++ b/include/uapi/drm/virtgpu_drm.h
> @@ -46,6 +46,11 @@ extern "C" {
>  #define DRM_VIRTGPU_TRANSFER_TO_HOST 0x07
>  #define DRM_VIRTGPU_WAIT     0x08
>  #define DRM_VIRTGPU_GET_CAPS  0x09
> +#define DRM_VIRTGPU_WINSRV_CONNECT  0x0a
> +#define DRM_VIRTGPU_WINSRV_TX  0x0b
> +#define DRM_VIRTGPU_WINSRV_RX  0x0c
> +
> +#define VIRTGPU_WINSRV_MAX_ALLOCS 28
>
>  struct drm_virtgpu_map {
>         __u64 offset; /* use for mmap system call */
> @@ -132,6 +137,18 @@ struct drm_virtgpu_get_caps {
>         __u32 pad;
>  };
>
> +struct drm_virtgpu_winsrv {
> +       __s32 fds[VIRTGPU_WINSRV_MAX_ALLOCS];
> +       __u64 data;
> +       __u32 len;
> +       __u32 pad;
> +};
> +
> +struct drm_virtgpu_winsrv_connect {
> +       __u32 fd;   /* returned by kernel */
> +       __u32 pad;
> +};
> +
>  #define DRM_IOCTL_VIRTGPU_MAP \
>         DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_MAP, struct drm_virtgpu_map)
>
> @@ -167,6 +184,18 @@ struct drm_virtgpu_get_caps {
>         DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_GET_CAPS, \
>         struct drm_virtgpu_get_caps)
>
> +#define DRM_IOCTL_VIRTGPU_WINSRV_CONNECT \
> +       DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_WINSRV_CONNECT, \
> +               struct drm_virtgpu_winsrv_connect)
> +
> +#define DRM_IOCTL_VIRTGPU_WINSRV_TX \
> +       DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_WINSRV_TX, \
> +               struct drm_virtgpu_winsrv)
> +
> +#define DRM_IOCTL_VIRTGPU_WINSRV_RX \
> +       DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_WINSRV_RX, \
> +               struct drm_virtgpu_winsrv)
> +
>  #if defined(__cplusplus)
>  }
>  #endif
> diff --git a/include/uapi/linux/virtio_gpu.h b/include/uapi/linux/virtio_gpu.h
> index 4b04ead26cd9..afe830bb8d00 100644
> --- a/include/uapi/linux/virtio_gpu.h
> +++ b/include/uapi/linux/virtio_gpu.h
> @@ -71,6 +71,12 @@ enum virtio_gpu_ctrl_type {
>         VIRTIO_GPU_CMD_UPDATE_CURSOR = 0x0300,
>         VIRTIO_GPU_CMD_MOVE_CURSOR,
>
> +       /* window server commands */
> +       VIRTIO_GPU_CMD_WINSRV_CONNECT = 0x0400,
> +       VIRTIO_GPU_CMD_WINSRV_DISCONNECT,
> +       VIRTIO_GPU_CMD_WINSRV_TX,
> +       VIRTIO_GPU_CMD_WINSRV_RX,
> +
>         /* success responses */
>         VIRTIO_GPU_RESP_OK_NODATA = 0x1100,
>         VIRTIO_GPU_RESP_OK_DISPLAY_INFO,
> @@ -290,6 +296,41 @@ struct virtio_gpu_resp_capset {
>         __u8 capset_data[];
>  };
>
> +/* VIRTIO_GPU_CMD_WINSRV_CONNECT */
> +struct virtio_gpu_winsrv_connect {
> +       struct virtio_gpu_ctrl_hdr hdr;
> +       __le32 client_fd;
> +       __le32 padding;
> +};
> +
> +/* VIRTIO_GPU_CMD_WINSRV_DISCONNECT */
> +struct virtio_gpu_winsrv_disconnect {
> +       struct virtio_gpu_ctrl_hdr hdr;
> +       __le32 client_fd;
> +       __le32 padding;
> +};
> +
> +#define VIRTIO_GPU_WINSRV_MAX_ALLOCS 28
> +#define VIRTIO_GPU_WINSRV_TX_MAX_DATA 4096
> +
> +/* VIRTIO_GPU_CMD_WINSRV_TX */
> +/* these commands are followed in the queue descriptor by protocol buffers */
> +struct virtio_gpu_winsrv_tx {
> +       struct virtio_gpu_ctrl_hdr hdr;
> +       __le32 client_fd;
> +       __le32 len;
> +       __le32 resource_ids[VIRTIO_GPU_WINSRV_MAX_ALLOCS];
> +};
> +
> +/* VIRTIO_GPU_CMD_WINSRV_RX */
> +struct virtio_gpu_winsrv_rx {
> +       struct virtio_gpu_ctrl_hdr hdr;
> +       __le32 client_fd;
> +       __u8 data[VIRTIO_GPU_WINSRV_TX_MAX_DATA];
> +       __le32 len;
> +       __le32 resource_ids[VIRTIO_GPU_WINSRV_MAX_ALLOCS];
> +};
> +
>  #define VIRTIO_GPU_EVENT_DISPLAY (1 << 0)
>
>  struct virtio_gpu_config {
> --
> 2.14.3
>
_______________________________________________
Virtualization mailing list
Virtualization@xxxxxxxxxxxxxxxxxxxxxxxxxx
https://lists.linuxfoundation.org/mailman/listinfo/virtualization



[Index of Archives]     [KVM Development]     [Libvirt Development]     [Libvirt Users]     [CentOS Virtualization]     [Netdev]     [Ethernet Bridging]     [Linux Wireless]     [Kernel Newbies]     [Security]     [Linux for Hams]     [Netfilter]     [Bugtraq]     [Yosemite Forum]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux Admin]     [Samba]

  Powered by Linux