Commit bc3e5f48 authored by Maciej Falkowski's avatar Maciej Falkowski Committed by Jacek Lawrynowicz
Browse files

accel/ivpu: Use workqueue for IRQ handling



Convert IRQ bottom half from the thread handler into workqueue.
This increases a stability in rare scenarios where driver on
debugging/hardening kernels processes IRQ too slow and misses
some interrupts due to it.
Workqueue handler also gives a very minor performance increase.

Signed-off-by: default avatarMaciej Falkowski <maciej.falkowski@linux.intel.com>
Reviewed-by: default avatarJacek Lawrynowicz <jacek.lawrynowicz@linux.intel.com>
Signed-off-by: default avatarJacek Lawrynowicz <jacek.lawrynowicz@linux.intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20250107173238.381120-6-maciej.falkowski@linux.intel.com
parent 7bfc9fa9
Loading
Loading
Loading
Loading
+10 −29
Original line number Diff line number Diff line
@@ -7,6 +7,7 @@
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/pm_runtime.h>
#include <linux/workqueue.h>
#include <generated/utsrelease.h>

#include <drm/drm_accel.h>
@@ -421,6 +422,9 @@ void ivpu_prepare_for_reset(struct ivpu_device *vdev)
{
	ivpu_hw_irq_disable(vdev);
	disable_irq(vdev->irq);
	cancel_work_sync(&vdev->irq_ipc_work);
	cancel_work_sync(&vdev->irq_dct_work);
	cancel_work_sync(&vdev->context_abort_work);
	ivpu_ipc_disable(vdev);
	ivpu_mmu_disable(vdev);
}
@@ -465,31 +469,6 @@ static const struct drm_driver driver = {
	.major = 1,
};

static irqreturn_t ivpu_irq_thread_handler(int irq, void *arg)
{
	struct ivpu_device *vdev = arg;
	u8 irq_src;

	if (kfifo_is_empty(&vdev->hw->irq.fifo))
		return IRQ_NONE;

	while (kfifo_get(&vdev->hw->irq.fifo, &irq_src)) {
		switch (irq_src) {
		case IVPU_HW_IRQ_SRC_IPC:
			ivpu_ipc_irq_thread_handler(vdev);
			break;
		case IVPU_HW_IRQ_SRC_DCT:
			ivpu_pm_dct_irq_thread_handler(vdev);
			break;
		default:
			ivpu_err_ratelimited(vdev, "Unknown IRQ source: %u\n", irq_src);
			break;
		}
	}

	return IRQ_HANDLED;
}

static int ivpu_irq_init(struct ivpu_device *vdev)
{
	struct pci_dev *pdev = to_pci_dev(vdev->drm.dev);
@@ -501,12 +480,16 @@ static int ivpu_irq_init(struct ivpu_device *vdev)
		return ret;
	}

	INIT_WORK(&vdev->irq_ipc_work, ivpu_ipc_irq_work_fn);
	INIT_WORK(&vdev->irq_dct_work, ivpu_pm_irq_dct_work_fn);
	INIT_WORK(&vdev->context_abort_work, ivpu_context_abort_work_fn);

	ivpu_irq_handlers_init(vdev);

	vdev->irq = pci_irq_vector(pdev, 0);

	ret = devm_request_threaded_irq(vdev->drm.dev, vdev->irq, ivpu_hw_irq_handler,
					ivpu_irq_thread_handler, IRQF_NO_AUTOEN, DRIVER_NAME, vdev);
	ret = devm_request_irq(vdev->drm.dev, vdev->irq, ivpu_hw_irq_handler,
			       IRQF_NO_AUTOEN, DRIVER_NAME, vdev);
	if (ret)
		ivpu_err(vdev, "Failed to request an IRQ %d\n", ret);

@@ -599,8 +582,6 @@ static int ivpu_dev_init(struct ivpu_device *vdev)
	vdev->db_limit.min = IVPU_MIN_DB;
	vdev->db_limit.max = IVPU_MAX_DB;

	INIT_WORK(&vdev->context_abort_work, ivpu_context_abort_thread_handler);

	ret = drmm_mutex_init(&vdev->drm, &vdev->context_list_lock);
	if (ret)
		goto err_xa_destroy;
+4 −1
Original line number Diff line number Diff line
@@ -137,12 +137,15 @@ struct ivpu_device {
	struct mutex context_list_lock; /* Protects user context addition/removal */
	struct xarray context_xa;
	struct xa_limit context_xa_limit;
	struct work_struct context_abort_work;

	struct xarray db_xa;
	struct xa_limit db_limit;
	u32 db_next;

	struct work_struct irq_ipc_work;
	struct work_struct irq_dct_work;
	struct work_struct context_abort_work;

	struct mutex bo_list_lock; /* Protects bo_list */
	struct list_head bo_list;

+0 −5
Original line number Diff line number Diff line
@@ -285,8 +285,6 @@ void ivpu_hw_profiling_freq_drive(struct ivpu_device *vdev, bool enable)

void ivpu_irq_handlers_init(struct ivpu_device *vdev)
{
	INIT_KFIFO(vdev->hw->irq.fifo);

	if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX)
		vdev->hw->irq.ip_irq_handler = ivpu_hw_ip_irq_handler_37xx;
	else
@@ -300,7 +298,6 @@ void ivpu_irq_handlers_init(struct ivpu_device *vdev)

void ivpu_hw_irq_enable(struct ivpu_device *vdev)
{
	kfifo_reset(&vdev->hw->irq.fifo);
	ivpu_hw_ip_irq_enable(vdev);
	ivpu_hw_btrs_irq_enable(vdev);
}
@@ -327,8 +324,6 @@ irqreturn_t ivpu_hw_irq_handler(int irq, void *ptr)
	/* Re-enable global interrupts to re-trigger MSI for pending interrupts */
	ivpu_hw_btrs_global_int_enable(vdev);

	if (!kfifo_is_empty(&vdev->hw->irq.fifo))
		return IRQ_WAKE_THREAD;
	if (ip_handled || btrs_handled)
		return IRQ_HANDLED;
	return IRQ_NONE;
+0 −9
Original line number Diff line number Diff line
@@ -6,18 +6,10 @@
#ifndef __IVPU_HW_H__
#define __IVPU_HW_H__

#include <linux/kfifo.h>

#include "ivpu_drv.h"
#include "ivpu_hw_btrs.h"
#include "ivpu_hw_ip.h"

#define IVPU_HW_IRQ_FIFO_LENGTH 1024

#define IVPU_HW_IRQ_SRC_IPC 1
#define IVPU_HW_IRQ_SRC_MMU_EVTQ 2
#define IVPU_HW_IRQ_SRC_DCT 3

struct ivpu_addr_range {
	resource_size_t start;
	resource_size_t end;
@@ -27,7 +19,6 @@ struct ivpu_hw_info {
	struct {
		bool (*btrs_irq_handler)(struct ivpu_device *vdev, int irq);
		bool (*ip_irq_handler)(struct ivpu_device *vdev, int irq);
		DECLARE_KFIFO(fifo, u8, IVPU_HW_IRQ_FIFO_LENGTH);
	} irq;
	struct {
		struct ivpu_addr_range global;
+1 −2
Original line number Diff line number Diff line
@@ -630,8 +630,7 @@ bool ivpu_hw_btrs_irq_handler_lnl(struct ivpu_device *vdev, int irq)

	if (REG_TEST_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, SURV_ERR, status)) {
		ivpu_dbg(vdev, IRQ, "Survivability IRQ\n");
		if (!kfifo_put(&vdev->hw->irq.fifo, IVPU_HW_IRQ_SRC_DCT))
			ivpu_err_ratelimited(vdev, "IRQ FIFO full\n");
		queue_work(system_wq, &vdev->irq_dct_work);
	}

	if (REG_TEST_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, FREQ_CHANGE, status))
Loading