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