xref: /openbsd-src/sys/dev/pci/drm/amd/amdkfd/kfd_interrupt.c (revision f005ef32267c16bdb134f0e9fa4477dbe07c263a)
11bb76ff1Sjsg // SPDX-License-Identifier: GPL-2.0 OR MIT
2fb4d8502Sjsg /*
31bb76ff1Sjsg  * Copyright 2014-2022 Advanced Micro Devices, Inc.
4fb4d8502Sjsg  *
5fb4d8502Sjsg  * Permission is hereby granted, free of charge, to any person obtaining a
6fb4d8502Sjsg  * copy of this software and associated documentation files (the "Software"),
7fb4d8502Sjsg  * to deal in the Software without restriction, including without limitation
8fb4d8502Sjsg  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9fb4d8502Sjsg  * and/or sell copies of the Software, and to permit persons to whom the
10fb4d8502Sjsg  * Software is furnished to do so, subject to the following conditions:
11fb4d8502Sjsg  *
12fb4d8502Sjsg  * The above copyright notice and this permission notice shall be included in
13fb4d8502Sjsg  * all copies or substantial portions of the Software.
14fb4d8502Sjsg  *
15fb4d8502Sjsg  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16fb4d8502Sjsg  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17fb4d8502Sjsg  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18fb4d8502Sjsg  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
19fb4d8502Sjsg  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20fb4d8502Sjsg  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
21fb4d8502Sjsg  * OTHER DEALINGS IN THE SOFTWARE.
22fb4d8502Sjsg  */
23fb4d8502Sjsg 
24fb4d8502Sjsg /*
25fb4d8502Sjsg  * KFD Interrupts.
26fb4d8502Sjsg  *
27fb4d8502Sjsg  * AMD GPUs deliver interrupts by pushing an interrupt description onto the
28fb4d8502Sjsg  * interrupt ring and then sending an interrupt. KGD receives the interrupt
29fb4d8502Sjsg  * in ISR and sends us a pointer to each new entry on the interrupt ring.
30fb4d8502Sjsg  *
31fb4d8502Sjsg  * We generally can't process interrupt-signaled events from ISR, so we call
32fb4d8502Sjsg  * out to each interrupt client module (currently only the scheduler) to ask if
33fb4d8502Sjsg  * each interrupt is interesting. If they return true, then it requires further
34fb4d8502Sjsg  * processing so we copy it to an internal interrupt ring and call each
35fb4d8502Sjsg  * interrupt client again from a work-queue.
36fb4d8502Sjsg  *
37fb4d8502Sjsg  * There's no acknowledgment for the interrupts we use. The hardware simply
38fb4d8502Sjsg  * queues a new interrupt each time without waiting.
39fb4d8502Sjsg  *
40fb4d8502Sjsg  * The fixed-size internal queue means that it's possible for us to lose
41fb4d8502Sjsg  * interrupts because we have no back-pressure to the hardware.
42fb4d8502Sjsg  */
43fb4d8502Sjsg 
44fb4d8502Sjsg #include <linux/slab.h>
45fb4d8502Sjsg #include <linux/device.h>
46fb4d8502Sjsg #include <linux/kfifo.h>
47fb4d8502Sjsg #include "kfd_priv.h"
48fb4d8502Sjsg 
49fb4d8502Sjsg #define KFD_IH_NUM_ENTRIES 8192
50fb4d8502Sjsg 
51fb4d8502Sjsg static void interrupt_wq(struct work_struct *);
52fb4d8502Sjsg 
kfd_interrupt_init(struct kfd_node * node)53*f005ef32Sjsg int kfd_interrupt_init(struct kfd_node *node)
54fb4d8502Sjsg {
55fb4d8502Sjsg 	int r;
56fb4d8502Sjsg 
57*f005ef32Sjsg 	r = kfifo_alloc(&node->ih_fifo,
58*f005ef32Sjsg 		KFD_IH_NUM_ENTRIES * node->kfd->device_info.ih_ring_entry_size,
59fb4d8502Sjsg 		GFP_KERNEL);
60fb4d8502Sjsg 	if (r) {
61*f005ef32Sjsg 		dev_err(node->adev->dev, "Failed to allocate IH fifo\n");
62fb4d8502Sjsg 		return r;
63fb4d8502Sjsg 	}
64fb4d8502Sjsg 
65*f005ef32Sjsg 	node->ih_wq = alloc_workqueue("KFD IH", WQ_HIGHPRI, 1);
66*f005ef32Sjsg 	if (unlikely(!node->ih_wq)) {
67*f005ef32Sjsg 		kfifo_free(&node->ih_fifo);
68*f005ef32Sjsg 		dev_err(node->adev->dev, "Failed to allocate KFD IH workqueue\n");
6954f904ebSjsg 		return -ENOMEM;
7054f904ebSjsg 	}
71*f005ef32Sjsg 	spin_lock_init(&node->interrupt_lock);
72fb4d8502Sjsg 
73*f005ef32Sjsg 	INIT_WORK(&node->interrupt_work, interrupt_wq);
74fb4d8502Sjsg 
75*f005ef32Sjsg 	node->interrupts_active = true;
76fb4d8502Sjsg 
77fb4d8502Sjsg 	/*
78fb4d8502Sjsg 	 * After this function returns, the interrupt will be enabled. This
79fb4d8502Sjsg 	 * barrier ensures that the interrupt running on a different processor
80fb4d8502Sjsg 	 * sees all the above writes.
81fb4d8502Sjsg 	 */
82fb4d8502Sjsg 	smp_wmb();
83fb4d8502Sjsg 
84fb4d8502Sjsg 	return 0;
85fb4d8502Sjsg }
86fb4d8502Sjsg 
kfd_interrupt_exit(struct kfd_node * node)87*f005ef32Sjsg void kfd_interrupt_exit(struct kfd_node *node)
88fb4d8502Sjsg {
89fb4d8502Sjsg 	/*
90fb4d8502Sjsg 	 * Stop the interrupt handler from writing to the ring and scheduling
91fb4d8502Sjsg 	 * workqueue items. The spinlock ensures that any interrupt running
92fb4d8502Sjsg 	 * after we have unlocked sees interrupts_active = false.
93fb4d8502Sjsg 	 */
94fb4d8502Sjsg 	unsigned long flags;
95fb4d8502Sjsg 
96*f005ef32Sjsg 	spin_lock_irqsave(&node->interrupt_lock, flags);
97*f005ef32Sjsg 	node->interrupts_active = false;
98*f005ef32Sjsg 	spin_unlock_irqrestore(&node->interrupt_lock, flags);
99fb4d8502Sjsg 
100fb4d8502Sjsg 	/*
101fb4d8502Sjsg 	 * flush_work ensures that there are no outstanding
102fb4d8502Sjsg 	 * work-queue items that will access interrupt_ring. New work items
103fb4d8502Sjsg 	 * can't be created because we stopped interrupt handling above.
104fb4d8502Sjsg 	 */
105*f005ef32Sjsg 	flush_workqueue(node->ih_wq);
106fb4d8502Sjsg 
107*f005ef32Sjsg 	kfifo_free(&node->ih_fifo);
108fb4d8502Sjsg }
109fb4d8502Sjsg 
110fb4d8502Sjsg /*
111fb4d8502Sjsg  * Assumption: single reader/writer. This function is not re-entrant
112fb4d8502Sjsg  */
enqueue_ih_ring_entry(struct kfd_node * node,const void * ih_ring_entry)113*f005ef32Sjsg bool enqueue_ih_ring_entry(struct kfd_node *node, const void *ih_ring_entry)
114fb4d8502Sjsg {
115fb4d8502Sjsg 	int count;
116fb4d8502Sjsg 
117*f005ef32Sjsg 	count = kfifo_in(&node->ih_fifo, ih_ring_entry,
118*f005ef32Sjsg 				node->kfd->device_info.ih_ring_entry_size);
119*f005ef32Sjsg 	if (count != node->kfd->device_info.ih_ring_entry_size) {
120*f005ef32Sjsg 		dev_dbg_ratelimited(node->adev->dev,
121fb4d8502Sjsg 			"Interrupt ring overflow, dropping interrupt %d\n",
122fb4d8502Sjsg 			count);
123fb4d8502Sjsg 		return false;
124fb4d8502Sjsg 	}
125fb4d8502Sjsg 
126fb4d8502Sjsg 	return true;
127fb4d8502Sjsg }
128fb4d8502Sjsg 
129fb4d8502Sjsg /*
130fb4d8502Sjsg  * Assumption: single reader/writer. This function is not re-entrant
131fb4d8502Sjsg  */
dequeue_ih_ring_entry(struct kfd_node * node,void * ih_ring_entry)132*f005ef32Sjsg static bool dequeue_ih_ring_entry(struct kfd_node *node, void *ih_ring_entry)
133fb4d8502Sjsg {
134fb4d8502Sjsg 	int count;
135fb4d8502Sjsg 
136*f005ef32Sjsg 	count = kfifo_out(&node->ih_fifo, ih_ring_entry,
137*f005ef32Sjsg 				node->kfd->device_info.ih_ring_entry_size);
138fb4d8502Sjsg 
139*f005ef32Sjsg 	WARN_ON(count && count != node->kfd->device_info.ih_ring_entry_size);
140fb4d8502Sjsg 
141*f005ef32Sjsg 	return count == node->kfd->device_info.ih_ring_entry_size;
142fb4d8502Sjsg }
143fb4d8502Sjsg 
interrupt_wq(struct work_struct * work)144fb4d8502Sjsg static void interrupt_wq(struct work_struct *work)
145fb4d8502Sjsg {
146*f005ef32Sjsg 	struct kfd_node *dev = container_of(work, struct kfd_node,
147fb4d8502Sjsg 						interrupt_work);
148fb4d8502Sjsg 	uint32_t ih_ring_entry[KFD_MAX_RING_ENTRY_SIZE];
1491bb76ff1Sjsg 	unsigned long start_jiffies = jiffies;
150fb4d8502Sjsg 
151*f005ef32Sjsg 	if (dev->kfd->device_info.ih_ring_entry_size > sizeof(ih_ring_entry)) {
1521bb76ff1Sjsg 		dev_err_once(dev->adev->dev, "Ring entry too small\n");
153fb4d8502Sjsg 		return;
154fb4d8502Sjsg 	}
155fb4d8502Sjsg 
1561bb76ff1Sjsg 	while (dequeue_ih_ring_entry(dev, ih_ring_entry)) {
157*f005ef32Sjsg 		dev->kfd->device_info.event_interrupt_class->interrupt_wq(dev,
158fb4d8502Sjsg 								ih_ring_entry);
1591bb76ff1Sjsg 		if (time_is_before_jiffies(start_jiffies + HZ)) {
1601bb76ff1Sjsg 			/* If we spent more than a second processing signals,
1611bb76ff1Sjsg 			 * reschedule the worker to avoid soft-lockup warnings
1621bb76ff1Sjsg 			 */
1631bb76ff1Sjsg 			queue_work(dev->ih_wq, &dev->interrupt_work);
1641bb76ff1Sjsg 			break;
1651bb76ff1Sjsg 		}
1661bb76ff1Sjsg 	}
167fb4d8502Sjsg }
168fb4d8502Sjsg 
interrupt_is_wanted(struct kfd_node * dev,const uint32_t * ih_ring_entry,uint32_t * patched_ihre,bool * flag)169*f005ef32Sjsg bool interrupt_is_wanted(struct kfd_node *dev,
170fb4d8502Sjsg 			const uint32_t *ih_ring_entry,
171fb4d8502Sjsg 			uint32_t *patched_ihre, bool *flag)
172fb4d8502Sjsg {
173fb4d8502Sjsg 	/* integer and bitwise OR so there is no boolean short-circuiting */
174fb4d8502Sjsg 	unsigned int wanted = 0;
175fb4d8502Sjsg 
176*f005ef32Sjsg 	wanted |= dev->kfd->device_info.event_interrupt_class->interrupt_isr(dev,
177fb4d8502Sjsg 					 ih_ring_entry, patched_ihre, flag);
178fb4d8502Sjsg 
179fb4d8502Sjsg 	return wanted != 0;
180fb4d8502Sjsg }
181