Virtio gpu supports the drm_panic module, which displays a message to
the screen when a kernel panic occurs. It is supported where it has
vmapped shmem BO.

Signed-off-by: Jocelyn Falempe <jfale...@redhat.com>
Signed-off-by: Ryosuke Yasuoka <ryasu...@redhat.com>
---
v7:
- Reject no vmapped shmem BO. Also, insert comment and commit log about
  it.
- Restore MAX_INLINE_* and VBUFFER_SIZE definitions to the old place

v6:
https://lore.kernel.org/all/CAHpthZo=R=Csru2P8NVY8YKaasb2RTyrMYni-=8ri4K=xd8...@mail.gmail.com/

Based on Dmitry's comment, fix the followings
- Reject external dmabufs backing the GEM object
- Allocate vbuf with kmem_cache_zalloc(..., GFP_ATOMIC) instead of
  drmm_kzalloc().

v5:
https://lore.kernel.org/all/CAHpthZrZ6DjsCQ4baQ80b2vOTdkR=vHDx=10W7DTS4ohxb6=p...@mail.gmail.com/

Based on Dmitry's comment, fix the followings
- Rename virtio_panic_buffer to panic_vbuf
- Remove some unnecessary dummy ret and return directly.
- Reject if the bo is VRAM BO
- Remove virtio_gpu_panic_put_vbuf() before notify
- Add description for panic buffer allocation
- Remove virtio_gpu_panic_object_array and use
  virtio_gpu_panic_array_alloc() to allocate objs instead of static
  allocation in stack.

v4:
https://lore.kernel.org/all/ec721548-0d47-4c40-9e9d-59f58e218...@redhat.com/

- As per Dmitry's comment, make virtio_panic_buffer private to
  virtio_gpu_device.

v3:
https://lore.kernel.org/all/09d9815c-9d5b-464b-9362-5b8232d36...@collabora.com/

- As per Jocelyn's comment, add a finite timeout 500usec in
  virtio_gpu_panic_put_vbuf() to avoid infinite loop

v2:
https://lore.kernel.org/all/d885913e-e81c-488e-8db8-e3f7fae13...@redhat.com/

- Remove unnecessary virtio_gpu_vbuffer_inline
- Remove reclaim_list and just call drm_gem_object_put() if there is an
   obj
- Don't wait for an event in virtio_gpu_panic_queue_ctrl_sgs and just
   return -ENOMEM. Also add error handlers for this error.
- Use virtio_gpu_panic_queue_fenced_ctrl_buffer() in
virtio_gpu_panic_cmd_resource_flush
- Remove fence and objs arguments because these are always NULL in panic
   handler.
- Rename virtio_gpu_panic_queue_fenced_ctrl_buffer to
   ..._queue_ctrl_buffer
- Rename virtio_gpu_panic_alloc_cmd to ..._panic_init_cmd

v1:
https://lore.kernel.org/all/c7a4a4cd-ce84-4e87-924d-c1c001fc5...@redhat.com/

 drivers/gpu/drm/virtio/virtgpu_drv.h   |  11 ++
 drivers/gpu/drm/virtio/virtgpu_gem.c   |  14 +++
 drivers/gpu/drm/virtio/virtgpu_plane.c |  96 ++++++++++++++++
 drivers/gpu/drm/virtio/virtgpu_vq.c    | 151 +++++++++++++++++++++++++
 4 files changed, 272 insertions(+)

diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h 
b/drivers/gpu/drm/virtio/virtgpu_drv.h
index f42ca9d8ed10..f17660a71a3e 100644
--- a/drivers/gpu/drm/virtio/virtgpu_drv.h
+++ b/drivers/gpu/drm/virtio/virtgpu_drv.h
@@ -310,6 +310,7 @@ int virtio_gpu_mode_dumb_create(struct drm_file *file_priv,
                                struct drm_device *dev,
                                struct drm_mode_create_dumb *args);
 
+struct virtio_gpu_object_array *virtio_gpu_panic_array_alloc(void);
 struct virtio_gpu_object_array *virtio_gpu_array_alloc(u32 nents);
 struct virtio_gpu_object_array*
 virtio_gpu_array_from_handles(struct drm_file *drm_file, u32 *handles, u32 
nents);
@@ -334,12 +335,21 @@ void virtio_gpu_cmd_create_resource(struct 
virtio_gpu_device *vgdev,
                                    struct virtio_gpu_fence *fence);
 void virtio_gpu_cmd_unref_resource(struct virtio_gpu_device *vgdev,
                                   struct virtio_gpu_object *bo);
+int virtio_gpu_panic_cmd_transfer_to_host_2d(struct virtio_gpu_device *vgdev,
+                                            uint64_t offset,
+                                            uint32_t width, uint32_t height,
+                                            uint32_t x, uint32_t y,
+                                            struct virtio_gpu_object_array 
*objs);
 void virtio_gpu_cmd_transfer_to_host_2d(struct virtio_gpu_device *vgdev,
                                        uint64_t offset,
                                        uint32_t width, uint32_t height,
                                        uint32_t x, uint32_t y,
                                        struct virtio_gpu_object_array *objs,
                                        struct virtio_gpu_fence *fence);
+void virtio_gpu_panic_cmd_resource_flush(struct virtio_gpu_device *vgdev,
+                                        uint32_t resource_id,
+                                        uint32_t x, uint32_t y,
+                                        uint32_t width, uint32_t height);
 void virtio_gpu_cmd_resource_flush(struct virtio_gpu_device *vgdev,
                                   uint32_t resource_id,
                                   uint32_t x, uint32_t y,
@@ -408,6 +418,7 @@ void virtio_gpu_ctrl_ack(struct virtqueue *vq);
 void virtio_gpu_cursor_ack(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_panic_notify(struct virtio_gpu_device *vgdev);
 void virtio_gpu_notify(struct virtio_gpu_device *vgdev);
 
 int
diff --git a/drivers/gpu/drm/virtio/virtgpu_gem.c 
b/drivers/gpu/drm/virtio/virtgpu_gem.c
index 5aab588fc400..dde8fc1a3689 100644
--- a/drivers/gpu/drm/virtio/virtgpu_gem.c
+++ b/drivers/gpu/drm/virtio/virtgpu_gem.c
@@ -148,6 +148,20 @@ void virtio_gpu_gem_object_close(struct drm_gem_object 
*obj,
        virtio_gpu_notify(vgdev);
 }
 
+/* For drm panic */
+struct virtio_gpu_object_array *virtio_gpu_panic_array_alloc(void)
+{
+       struct virtio_gpu_object_array *objs;
+
+       objs = kmalloc(sizeof(struct virtio_gpu_object_array), GFP_ATOMIC);
+       if (!objs)
+               return NULL;
+
+       objs->nents = 0;
+       objs->total = 1;
+       return objs;
+}
+
 struct virtio_gpu_object_array *virtio_gpu_array_alloc(u32 nents)
 {
        struct virtio_gpu_object_array *objs;
diff --git a/drivers/gpu/drm/virtio/virtgpu_plane.c 
b/drivers/gpu/drm/virtio/virtgpu_plane.c
index 42aa554eca9f..a6f5a78f436a 100644
--- a/drivers/gpu/drm/virtio/virtgpu_plane.c
+++ b/drivers/gpu/drm/virtio/virtgpu_plane.c
@@ -28,6 +28,8 @@
 #include <drm/drm_fourcc.h>
 #include <drm/drm_gem_atomic_helper.h>
 #include <linux/virtio_dma_buf.h>
+#include <drm/drm_managed.h>
+#include <drm/drm_panic.h>
 
 #include "virtgpu_drv.h"
 
@@ -127,6 +129,30 @@ static int virtio_gpu_plane_atomic_check(struct drm_plane 
*plane,
        return ret;
 }
 
+/* For drm panic */
+static int virtio_gpu_panic_update_dumb_bo(struct virtio_gpu_device *vgdev,
+                                          struct drm_plane_state *state,
+                                          struct drm_rect *rect)
+{
+       struct virtio_gpu_object *bo =
+               gem_to_virtio_gpu_obj(state->fb->obj[0]);
+       struct virtio_gpu_object_array *objs;
+       uint32_t w = rect->x2 - rect->x1;
+       uint32_t h = rect->y2 - rect->y1;
+       uint32_t x = rect->x1;
+       uint32_t y = rect->y1;
+       uint32_t off = x * state->fb->format->cpp[0] +
+               y * state->fb->pitches[0];
+
+       objs = virtio_gpu_panic_array_alloc();
+       if (!objs)
+               return -ENOMEM;
+       virtio_gpu_array_add_obj(objs, &bo->base.base);
+
+       return virtio_gpu_panic_cmd_transfer_to_host_2d(vgdev, off, w, h, x, y,
+                                                       objs);
+}
+
 static void virtio_gpu_update_dumb_bo(struct virtio_gpu_device *vgdev,
                                      struct drm_plane_state *state,
                                      struct drm_rect *rect)
@@ -150,6 +176,24 @@ static void virtio_gpu_update_dumb_bo(struct 
virtio_gpu_device *vgdev,
                                           objs, NULL);
 }
 
+/* For drm_panic */
+static void virtio_gpu_panic_resource_flush(struct drm_plane *plane,
+                                           uint32_t x, uint32_t y,
+                                           uint32_t width, uint32_t height)
+{
+       struct drm_device *dev = plane->dev;
+       struct virtio_gpu_device *vgdev = dev->dev_private;
+       struct virtio_gpu_framebuffer *vgfb;
+       struct virtio_gpu_object *bo;
+
+       vgfb = to_virtio_gpu_framebuffer(plane->state->fb);
+       bo = gem_to_virtio_gpu_obj(vgfb->base.obj[0]);
+
+       virtio_gpu_panic_cmd_resource_flush(vgdev, bo->hw_res_handle, x, y,
+                                           width, height);
+       virtio_gpu_panic_notify(vgdev);
+}
+
 static void virtio_gpu_resource_flush(struct drm_plane *plane,
                                      uint32_t x, uint32_t y,
                                      uint32_t width, uint32_t height)
@@ -446,11 +490,63 @@ static void virtio_gpu_cursor_plane_update(struct 
drm_plane *plane,
        virtio_gpu_cursor_ping(vgdev, output);
 }
 
+static int virtio_drm_get_scanout_buffer(struct drm_plane *plane,
+                                        struct drm_scanout_buffer *sb)
+{
+       struct virtio_gpu_object *bo;
+
+       if (!plane->state || !plane->state->fb || !plane->state->visible)
+               return -ENODEV;
+
+       bo = gem_to_virtio_gpu_obj(plane->state->fb->obj[0]);
+
+       /* Only support mapped shmem bo */
+       if (virtio_gpu_is_vram(bo) || bo->base.base.import_attach || 
!bo->base.vaddr)
+               return -ENODEV;
+
+       iosys_map_set_vaddr(&sb->map[0], bo->base.vaddr);
+
+       sb->format = plane->state->fb->format;
+       sb->height = plane->state->fb->height;
+       sb->width = plane->state->fb->width;
+       sb->pitch[0] = plane->state->fb->pitches[0];
+       return 0;
+}
+
+static void virtio_panic_flush(struct drm_plane *plane)
+{
+       struct virtio_gpu_object *bo;
+       struct drm_device *dev = plane->dev;
+       struct virtio_gpu_device *vgdev = dev->dev_private;
+       struct drm_rect rect;
+
+       rect.x1 = 0;
+       rect.y1 = 0;
+       rect.x2 = plane->state->fb->width;
+       rect.y2 = plane->state->fb->height;
+
+       bo = gem_to_virtio_gpu_obj(plane->state->fb->obj[0]);
+
+       if (bo->dumb) {
+               if (virtio_gpu_panic_update_dumb_bo(vgdev, plane->state,
+                                                   &rect))
+                       return;
+       }
+
+       virtio_gpu_panic_resource_flush(plane,
+                                       plane->state->src_x >> 16,
+                                       plane->state->src_y >> 16,
+                                       plane->state->src_w >> 16,
+                                       plane->state->src_h >> 16);
+}
+
 static const struct drm_plane_helper_funcs virtio_gpu_primary_helper_funcs = {
        .prepare_fb             = virtio_gpu_plane_prepare_fb,
        .cleanup_fb             = virtio_gpu_plane_cleanup_fb,
        .atomic_check           = virtio_gpu_plane_atomic_check,
        .atomic_update          = virtio_gpu_primary_plane_update,
+       .get_scanout_buffer     = virtio_drm_get_scanout_buffer,
+       .panic_flush            = virtio_panic_flush,
 };
 
 static const struct drm_plane_helper_funcs virtio_gpu_cursor_helper_funcs = {
diff --git a/drivers/gpu/drm/virtio/virtgpu_vq.c 
b/drivers/gpu/drm/virtio/virtgpu_vq.c
index ad91624df42d..efac4f244d85 100644
--- a/drivers/gpu/drm/virtio/virtgpu_vq.c
+++ b/drivers/gpu/drm/virtio/virtgpu_vq.c
@@ -86,6 +86,22 @@ void virtio_gpu_free_vbufs(struct virtio_gpu_device *vgdev)
        vgdev->vbufs = NULL;
 }
 
+/* For drm_panic */
+static struct virtio_gpu_vbuffer*
+virtio_gpu_panic_get_vbuf(struct virtio_gpu_device *vgdev, int size)
+{
+       struct virtio_gpu_vbuffer *vbuf;
+
+       vbuf = kmem_cache_zalloc(vgdev->vbufs, GFP_ATOMIC);
+
+       vbuf->buf = (void *)vbuf + sizeof(*vbuf);
+       vbuf->size = size;
+       vbuf->resp_cb = NULL;
+       vbuf->resp_size = sizeof(struct virtio_gpu_ctrl_hdr);
+       vbuf->resp_buf = (void *)vbuf->buf + size;
+       return vbuf;
+}
+
 static struct virtio_gpu_vbuffer*
 virtio_gpu_get_vbuf(struct virtio_gpu_device *vgdev,
                    int size, int resp_size, void *resp_buf,
@@ -137,6 +153,18 @@ virtio_gpu_alloc_cursor(struct virtio_gpu_device *vgdev,
        return (struct virtio_gpu_update_cursor *)vbuf->buf;
 }
 
+/* For drm_panic */
+static void *virtio_gpu_panic_alloc_cmd_resp(struct virtio_gpu_device *vgdev,
+                                            struct virtio_gpu_vbuffer 
**vbuffer_p,
+                                            int cmd_size)
+{
+       struct virtio_gpu_vbuffer *vbuf;
+
+       vbuf = virtio_gpu_panic_get_vbuf(vgdev, cmd_size);
+       *vbuffer_p = vbuf;
+       return (struct virtio_gpu_command *)vbuf->buf;
+}
+
 static void *virtio_gpu_alloc_cmd_resp(struct virtio_gpu_device *vgdev,
                                       virtio_gpu_resp_cb cb,
                                       struct virtio_gpu_vbuffer **vbuffer_p,
@@ -311,6 +339,34 @@ static struct sg_table *vmalloc_to_sgt(char *data, 
uint32_t size, int *sg_ents)
        return sgt;
 }
 
+/* For drm_panic */
+static int virtio_gpu_panic_queue_ctrl_sgs(struct virtio_gpu_device *vgdev,
+                                          struct virtio_gpu_vbuffer *vbuf,
+                                          int elemcnt,
+                                          struct scatterlist **sgs,
+                                          int outcnt,
+                                          int incnt)
+{
+       struct virtqueue *vq = vgdev->ctrlq.vq;
+       int ret;
+
+       if (vgdev->has_indirect)
+               elemcnt = 1;
+
+       if (vq->num_free < elemcnt)
+               return -ENOMEM;
+
+       ret = virtqueue_add_sgs(vq, sgs, outcnt, incnt, vbuf, GFP_ATOMIC);
+       WARN_ON(ret);
+
+       vbuf->seqno = ++vgdev->ctrlq.seqno;
+       trace_virtio_gpu_cmd_queue(vq, virtio_gpu_vbuf_ctrl_hdr(vbuf), 
vbuf->seqno);
+
+       atomic_inc(&vgdev->pending_commands);
+
+       return 0;
+}
+
 static int virtio_gpu_queue_ctrl_sgs(struct virtio_gpu_device *vgdev,
                                     struct virtio_gpu_vbuffer *vbuf,
                                     struct virtio_gpu_fence *fence,
@@ -368,6 +424,32 @@ static int virtio_gpu_queue_ctrl_sgs(struct 
virtio_gpu_device *vgdev,
        return 0;
 }
 
+/* For drm_panic */
+static int virtio_gpu_panic_queue_ctrl_buffer(struct virtio_gpu_device *vgdev,
+                                             struct virtio_gpu_vbuffer *vbuf)
+{
+       struct scatterlist *sgs[3], vcmd, vresp;
+       int elemcnt = 0, outcnt = 0, incnt = 0;
+
+       /* set up vcmd */
+       sg_init_one(&vcmd, vbuf->buf, vbuf->size);
+       elemcnt++;
+       sgs[outcnt] = &vcmd;
+       outcnt++;
+
+       /* set up vresp */
+       if (vbuf->resp_size) {
+               sg_init_one(&vresp, vbuf->resp_buf, vbuf->resp_size);
+               elemcnt++;
+               sgs[outcnt + incnt] = &vresp;
+               incnt++;
+       }
+
+       return virtio_gpu_panic_queue_ctrl_sgs(vgdev, vbuf,
+                                              elemcnt, sgs,
+                                              outcnt, incnt);
+}
+
 static int virtio_gpu_queue_fenced_ctrl_buffer(struct virtio_gpu_device *vgdev,
                                               struct virtio_gpu_vbuffer *vbuf,
                                               struct virtio_gpu_fence *fence)
@@ -422,6 +504,21 @@ static int virtio_gpu_queue_fenced_ctrl_buffer(struct 
virtio_gpu_device *vgdev,
        return ret;
 }
 
+/* For drm_panic */
+void virtio_gpu_panic_notify(struct virtio_gpu_device *vgdev)
+{
+       bool notify;
+
+       if (!atomic_read(&vgdev->pending_commands))
+               return;
+
+       atomic_set(&vgdev->pending_commands, 0);
+       notify = virtqueue_kick_prepare(vgdev->ctrlq.vq);
+
+       if (notify)
+               virtqueue_notify(vgdev->ctrlq.vq);
+}
+
 void virtio_gpu_notify(struct virtio_gpu_device *vgdev)
 {
        bool notify;
@@ -567,6 +664,29 @@ void virtio_gpu_cmd_set_scanout(struct virtio_gpu_device 
*vgdev,
        virtio_gpu_queue_ctrl_buffer(vgdev, vbuf);
 }
 
+/* For drm_panic */
+void virtio_gpu_panic_cmd_resource_flush(struct virtio_gpu_device *vgdev,
+                                         uint32_t resource_id,
+                                         uint32_t x, uint32_t y,
+                                         uint32_t width, uint32_t height)
+{
+       struct virtio_gpu_resource_flush *cmd_p;
+       struct virtio_gpu_vbuffer *vbuf;
+
+       cmd_p = virtio_gpu_panic_alloc_cmd_resp(vgdev, &vbuf, sizeof(*cmd_p));
+       memset(cmd_p, 0, sizeof(*cmd_p));
+       vbuf->objs = NULL;
+
+       cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_RESOURCE_FLUSH);
+       cmd_p->resource_id = cpu_to_le32(resource_id);
+       cmd_p->r.width = cpu_to_le32(width);
+       cmd_p->r.height = cpu_to_le32(height);
+       cmd_p->r.x = cpu_to_le32(x);
+       cmd_p->r.y = cpu_to_le32(y);
+
+       virtio_gpu_panic_queue_ctrl_buffer(vgdev, vbuf);
+}
+
 void virtio_gpu_cmd_resource_flush(struct virtio_gpu_device *vgdev,
                                   uint32_t resource_id,
                                   uint32_t x, uint32_t y,
@@ -591,6 +711,37 @@ void virtio_gpu_cmd_resource_flush(struct 
virtio_gpu_device *vgdev,
        virtio_gpu_queue_fenced_ctrl_buffer(vgdev, vbuf, fence);
 }
 
+/* For drm_panic */
+int virtio_gpu_panic_cmd_transfer_to_host_2d(struct virtio_gpu_device *vgdev,
+                                            uint64_t offset,
+                                            uint32_t width, uint32_t height,
+                                            uint32_t x, uint32_t y,
+                                            struct virtio_gpu_object_array 
*objs)
+{
+       struct virtio_gpu_object *bo = gem_to_virtio_gpu_obj(objs->objs[0]);
+       struct virtio_gpu_transfer_to_host_2d *cmd_p;
+       struct virtio_gpu_vbuffer *vbuf;
+       bool use_dma_api = !virtio_has_dma_quirk(vgdev->vdev);
+
+       if (virtio_gpu_is_shmem(bo) && use_dma_api)
+               dma_sync_sgtable_for_device(vgdev->vdev->dev.parent,
+                                           bo->base.sgt, DMA_TO_DEVICE);
+
+       cmd_p = virtio_gpu_panic_alloc_cmd_resp(vgdev, &vbuf, sizeof(*cmd_p));
+       memset(cmd_p, 0, sizeof(*cmd_p));
+       vbuf->objs = objs;
+
+       cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D);
+       cmd_p->resource_id = cpu_to_le32(bo->hw_res_handle);
+       cmd_p->offset = cpu_to_le64(offset);
+       cmd_p->r.width = cpu_to_le32(width);
+       cmd_p->r.height = cpu_to_le32(height);
+       cmd_p->r.x = cpu_to_le32(x);
+       cmd_p->r.y = cpu_to_le32(y);
+
+       return virtio_gpu_panic_queue_ctrl_buffer(vgdev, vbuf);
+}
+
 void virtio_gpu_cmd_transfer_to_host_2d(struct virtio_gpu_device *vgdev,
                                        uint64_t offset,
                                        uint32_t width, uint32_t height,

base-commit: 2014c95afecee3e76ca4a56956a936e23283f05b
-- 
2.47.1

Reply via email to