⬢ DragonFlyBSD Kernel Audit
DF-4732 / fix.diff
← back to finding ↓ download raw
--- a/sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm.h
+++ b/sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm.h
@@ -115,7 +115,10 @@ struct amdgpu_display_manager {
 	struct common_irq_params
 	vblank_params[DC_IRQ_SOURCE_VBLANK6 - DC_IRQ_SOURCE_VBLANK1 + 1];
 
-	/* this spin lock synchronizes access to 'irq_handler_list_table' */
-	spinlock_t irq_handler_list_table_lock;
+	/*
+	 * This spin lock synchronizes access to 'irq_handler_list_table'.
+	 * It is a true DragonFly spinlock: it is acquired from the GPU
+	 * interrupt dispatch path (amdgpu_dm_irq_immediate_work /
+	 * amdgpu_dm_irq_schedule_work), where sleeping (lockmgr) is illegal.
+	 */
+	struct spinlock irq_handler_list_table_lock;
 
 	struct backlight_device *backlight_dev;
--- a/sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c
+++ b/sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c
@@ -50,10 +50,18 @@ struct amdgpu_dm_irq_handler_data {
 	enum dc_irq_source irq_source;
 };
 
+/*
+ * The table lock is taken from the GPU ISR dispatch path (see
+ * amdgpu_dm_irq_handler below), which runs in serialized interrupt-thread
+ * context via request_irq()->bus_setup_intr().  The Linux shim's
+ * spin_lock_irqsave() maps to a *sleepable* lockmgr acquisition, which may
+ * not be performed from an ithread.  Use a true DragonFly spinlock wrapped
+ * in a critical section instead (equivalent to upstream's irqsave spinlock).
+ */
 #define DM_IRQ_TABLE_LOCK(adev, flags) \
-	spin_lock_irqsave(&adev->dm.irq_handler_list_table_lock, flags)
+	do { \
+		crit_enter(); \
+		spin_lock(&adev->dm.irq_handler_list_table_lock); \
+		(void)(flags); \
+	} while (0)
 
 #define DM_IRQ_TABLE_UNLOCK(adev, flags) \
-	spin_unlock_irqrestore(&adev->dm.irq_handler_list_table_lock, flags)
+	do { \
+		spin_unlock(&adev->dm.irq_handler_list_table_lock); \
+		crit_exit(); \
+	} while (0)
@@ -303,8 +311,7 @@ int amdgpu_dm_irq_init(struct amdgpu_device *adev)
 
 	DRM_DEBUG_KMS("DM_IRQ\n");
 
-	lockinit(&adev->dm.irq_handler_list_table_lock, "agdmihltl", 0, LK_CANRECURSE);
+	spin_init(&adev->dm.irq_handler_list_table_lock, "agdmihltl");
 
 	for (src = 0; src < DAL_IRQ_SOURCES_NUMBER; src++) {
 		/* low context handler list init */