[RFC 1/2] drm: Add sysrq key to kill current job on GPU
From: Rob Clark
Date: Mon Mar 16 2026 - 18:40:45 EST
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 <robin.clark@xxxxxxxxxxxxxxxx>
---
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