If your compositor is getting starved for GPU time, it is useful to have
a way to kill the current thing that is hogging the GPU.

Signed-off-by: Rob Clark <[email protected]>
---
 drivers/gpu/drm/Makefile        |  1 +
 drivers/gpu/drm/drm_dev_sysrq.c | 67 +++++++++++++++++++++++++++++++++
 drivers/gpu/drm/drm_drv.c       |  3 ++
 drivers/gpu/drm/drm_internal.h  | 11 ++++++
 include/drm/drm_device.h        |  8 ++++
 include/drm/drm_drv.h           |  7 ++++
 6 files changed, 97 insertions(+)
 create mode 100644 drivers/gpu/drm/drm_dev_sysrq.c

diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 0e1c668b46d2..337af859753f 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -44,6 +44,7 @@ drm-y := \
        drm_colorop.o \
        drm_connector.o \
        drm_crtc.o \
+       drm_dev_sysrq.o \
        drm_displayid.o \
        drm_drv.o \
        drm_dumb_buffers.o \
diff --git a/drivers/gpu/drm/drm_dev_sysrq.c b/drivers/gpu/drm/drm_dev_sysrq.c
new file mode 100644
index 000000000000..47e029b7cd0b
--- /dev/null
+++ b/drivers/gpu/drm/drm_dev_sysrq.c
@@ -0,0 +1,67 @@
+// SPDX-License-Identifier: GPL-2.0 or MIT
+
+#include <linux/sysrq.h>
+
+#include <drm/drm_device.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_print.h>
+
+#include "drm_internal.h"
+
+#ifdef CONFIG_MAGIC_SYSRQ
+static LIST_HEAD(drm_dev_sysrq_dev_list);
+static DEFINE_MUTEX(drm_dev_sysrq_dev_lock);
+
+/* emergency restore, don't bother with error reporting */
+static void drm_dev_sysrq_restore_work_fn(struct work_struct *ignored)
+{
+       struct drm_device *dev;
+
+       guard(mutex)(&drm_dev_sysrq_dev_lock);
+
+       list_for_each_entry(dev, &drm_dev_sysrq_dev_list, dev_sysrq_list) {
+               dev->driver->sysrq_kill(dev);
+       }
+}
+
+static DECLARE_WORK(drm_dev_sysrq_restore_work, drm_dev_sysrq_restore_work_fn);
+
+static void drm_dev_sysrq_restore_handler(u8 ignored)
+{
+       schedule_work(&drm_dev_sysrq_restore_work);
+}
+
+static const struct sysrq_key_op drm_dev_sysrq_kill_op = {
+       .handler = drm_dev_sysrq_restore_handler,
+       .help_msg = "kill-gpu-job(G)",
+       .action_msg = "Kill current job on the GPU",
+};
+
+void drm_dev_sysrq_register(struct drm_device *dev)
+{
+       const struct drm_driver *driver = dev->driver;
+
+       if (!driver->sysrq_kill)
+               return;
+
+       guard(mutex)(&drm_dev_sysrq_dev_lock);
+
+       if (list_empty(&drm_dev_sysrq_dev_list))
+               register_sysrq_key('G', &drm_dev_sysrq_kill_op);
+
+       list_add(&dev->dev_sysrq_list, &drm_dev_sysrq_dev_list);
+}
+
+void drm_dev_sysrq_unregister(struct drm_device *dev)
+{
+       guard(mutex)(&drm_dev_sysrq_dev_lock);
+
+       /* remove device from global restore list */
+       if (!drm_WARN_ON(dev, list_empty(&dev->dev_sysrq_list)))
+               list_del(&dev->dev_sysrq_list);
+
+       /* no devices left; unregister key */
+       if (list_empty(&drm_dev_sysrq_dev_list))
+               unregister_sysrq_key('G', &drm_dev_sysrq_kill_op);
+}
+#endif
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index 2915118436ce..c1f5a4ee6d58 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -734,6 +734,7 @@ static int drm_dev_init(struct drm_device *dev,
        INIT_LIST_HEAD(&dev->filelist_internal);
        INIT_LIST_HEAD(&dev->clientlist);
        INIT_LIST_HEAD(&dev->client_sysrq_list);
+       INIT_LIST_HEAD(&dev->dev_sysrq_list);
        INIT_LIST_HEAD(&dev->vblank_event_list);
 
        spin_lock_init(&dev->event_lock);
@@ -1102,6 +1103,7 @@ int drm_dev_register(struct drm_device *dev, unsigned 
long flags)
        }
        drm_panic_register(dev);
        drm_client_sysrq_register(dev);
+       drm_dev_sysrq_register(dev);
 
        DRM_INFO("Initialized %s %d.%d.%d for %s on minor %d\n",
                 driver->name, driver->major, driver->minor,
@@ -1146,6 +1148,7 @@ void drm_dev_unregister(struct drm_device *dev)
 {
        dev->registered = false;
 
+       drm_dev_sysrq_unregister(dev);
        drm_client_sysrq_unregister(dev);
        drm_panic_unregister(dev);
 
diff --git a/drivers/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h
index f893b1e3a596..164ff588aea4 100644
--- a/drivers/gpu/drm/drm_internal.h
+++ b/drivers/gpu/drm/drm_internal.h
@@ -67,6 +67,17 @@ static inline void drm_client_sysrq_unregister(struct 
drm_device *dev)
 { }
 #endif
 
+/* drm_dev_sysrq.c */
+#ifdef CONFIG_MAGIC_SYSRQ
+void drm_dev_sysrq_register(struct drm_device *dev);
+void drm_dev_sysrq_unregister(struct drm_device *dev);
+#else
+static inline void drm_dev_sysrq_register(struct drm_device *dev)
+{ }
+static inline void drm_dev_sysrq_unregister(struct drm_device *dev)
+{ }
+#endif
+
 /* drm_file.c */
 extern struct mutex drm_global_mutex;
 bool drm_dev_needs_global_mutex(struct drm_device *dev);
diff --git a/include/drm/drm_device.h b/include/drm/drm_device.h
index 5af49c5c3778..c6c0987dba36 100644
--- a/include/drm/drm_device.h
+++ b/include/drm/drm_device.h
@@ -246,6 +246,14 @@ struct drm_device {
         */
        struct list_head client_sysrq_list;
 
+       /**
+        * @dev_sysrq_list:
+        *
+        * Entry into list of devices registered for sysrq to kill current
+        * GPU job.
+        */
+       struct list_head dev_sysrq_list;
+
        /**
         * @vblank_disable_immediate:
         *
diff --git a/include/drm/drm_drv.h b/include/drm/drm_drv.h
index 42fc085f986d..8e9d5d597451 100644
--- a/include/drm/drm_drv.h
+++ b/include/drm/drm_drv.h
@@ -385,6 +385,13 @@ struct drm_driver {
        int (*fbdev_probe)(struct drm_fb_helper *fbdev_helper,
                           struct drm_fb_helper_surface_size *sizes);
 
+       /**
+        * @sysrq_kill:
+        *
+        * Handler for magic sysrq key to kill current job on the GPU.
+        */
+       void (*sysrq_kill)(struct drm_device *dev);
+
        /**
         * @show_fdinfo:
         *
-- 
2.53.0

Reply via email to