Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 04ad47bd authored by Andres Rodriguez's avatar Andres Rodriguez Committed by Oded Gabbay
Browse files

drm/amdkfd: use standard kernel kfifo for IH



Replace our implementation of a lockless ring buffer with the standard
linux kernel kfifo.

We shouldn't maintain our own version of a standard data structure.

Signed-off-by: default avatarAndres Rodriguez <andres.rodriguez@amd.com>
Signed-off-by: default avatarFelix Kuehling <Felix.Kuehling@amd.com>
Acked-by: default avatarOded Gabbay <oded.gabbay@gmail.com>
Signed-off-by: default avatarOded Gabbay <oded.gabbay@gmail.com>
parent 7e86a365
Loading
Loading
Loading
Loading
+25 −53
Original line number Diff line number Diff line
@@ -42,25 +42,24 @@

#include <linux/slab.h>
#include <linux/device.h>
#include <linux/kfifo.h>
#include "kfd_priv.h"

#define KFD_INTERRUPT_RING_SIZE 1024
#define KFD_IH_NUM_ENTRIES 1024

static void interrupt_wq(struct work_struct *);

int kfd_interrupt_init(struct kfd_dev *kfd)
{
	void *interrupt_ring = kmalloc_array(KFD_INTERRUPT_RING_SIZE,
					kfd->device_info->ih_ring_entry_size,
					GFP_KERNEL);
	if (!interrupt_ring)
		return -ENOMEM;
	int r;

	kfd->interrupt_ring = interrupt_ring;
	kfd->interrupt_ring_size =
		KFD_INTERRUPT_RING_SIZE * kfd->device_info->ih_ring_entry_size;
	atomic_set(&kfd->interrupt_ring_wptr, 0);
	atomic_set(&kfd->interrupt_ring_rptr, 0);
	r = kfifo_alloc(&kfd->ih_fifo,
		KFD_IH_NUM_ENTRIES * kfd->device_info->ih_ring_entry_size,
		GFP_KERNEL);
	if (r) {
		dev_err(kfd_chardev(), "Failed to allocate IH fifo\n");
		return r;
	}

	spin_lock_init(&kfd->interrupt_lock);

@@ -98,68 +97,41 @@ void kfd_interrupt_exit(struct kfd_dev *kfd)
	 */
	flush_scheduled_work();

	kfree(kfd->interrupt_ring);
	kfifo_free(&kfd->ih_fifo);
}

/*
 * This assumes that it can't be called concurrently with itself
 * but only with dequeue_ih_ring_entry.
 * Assumption: single reader/writer. This function is not re-entrant
 */
bool enqueue_ih_ring_entry(struct kfd_dev *kfd,	const void *ih_ring_entry)
{
	unsigned int rptr = atomic_read(&kfd->interrupt_ring_rptr);
	unsigned int wptr = atomic_read(&kfd->interrupt_ring_wptr);
	int count;

	if ((rptr - wptr) % kfd->interrupt_ring_size ==
					kfd->device_info->ih_ring_entry_size) {
		/* This is very bad, the system is likely to hang. */
	count = kfifo_in(&kfd->ih_fifo, ih_ring_entry,
				kfd->device_info->ih_ring_entry_size);
	if (count != kfd->device_info->ih_ring_entry_size) {
		dev_err_ratelimited(kfd_chardev(),
			"Interrupt ring overflow, dropping interrupt.\n");
			"Interrupt ring overflow, dropping interrupt %d\n",
			count);
		return false;
	}

	memcpy(kfd->interrupt_ring + wptr, ih_ring_entry,
			kfd->device_info->ih_ring_entry_size);

	wptr = (wptr + kfd->device_info->ih_ring_entry_size) %
			kfd->interrupt_ring_size;
	smp_wmb(); /* Ensure memcpy'd data is visible before wptr update. */
	atomic_set(&kfd->interrupt_ring_wptr, wptr);

	return true;
}

/*
 * This assumes that it can't be called concurrently with itself
 * but only with enqueue_ih_ring_entry.
 * Assumption: single reader/writer. This function is not re-entrant
 */
static bool dequeue_ih_ring_entry(struct kfd_dev *kfd, void *ih_ring_entry)
{
	/*
	 * Assume that wait queues have an implicit barrier, i.e. anything that
	 * happened in the ISR before it queued work is visible.
	 */

	unsigned int wptr = atomic_read(&kfd->interrupt_ring_wptr);
	unsigned int rptr = atomic_read(&kfd->interrupt_ring_rptr);

	if (rptr == wptr)
		return false;
	int count;

	memcpy(ih_ring_entry, kfd->interrupt_ring + rptr,
	count = kfifo_out(&kfd->ih_fifo, ih_ring_entry,
				kfd->device_info->ih_ring_entry_size);

	rptr = (rptr + kfd->device_info->ih_ring_entry_size) %
			kfd->interrupt_ring_size;
	WARN_ON(count && count != kfd->device_info->ih_ring_entry_size);

	/*
	 * Ensure the rptr write update is not visible until
	 * memcpy has finished reading.
	 */
	smp_mb();
	atomic_set(&kfd->interrupt_ring_rptr, rptr);

	return true;
	return count == kfd->device_info->ih_ring_entry_size;
}

static void interrupt_wq(struct work_struct *work)
+2 −4
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@
#include <linux/spinlock.h>
#include <linux/kfd_ioctl.h>
#include <linux/idr.h>
#include <linux/kfifo.h>
#include <kgd_kfd_interface.h>

#include "amd_shared.h"
@@ -182,10 +183,7 @@ struct kfd_dev {
	unsigned int gtt_sa_num_of_chunks;

	/* Interrupts */
	void *interrupt_ring;
	size_t interrupt_ring_size;
	atomic_t interrupt_ring_rptr;
	atomic_t interrupt_ring_wptr;
	struct kfifo ih_fifo;
	struct work_struct interrupt_work;
	spinlock_t interrupt_lock;