Commit 41a2d828 authored by Jacek Lawrynowicz's avatar Jacek Lawrynowicz
Browse files

accel/ivpu: Fix error handling in recovery/reset



Disable runtime PM for the duration of reset/recovery so it is possible
to set the correct runtime PM state depending on the outcome of the
`ivpu_resume()`. Don’t suspend or reset the HW if the NPU is suspended
when the reset/recovery is requested. Also, move common reset/recovery
code to separate functions for better code readability.

Fixes: 27d19268 ("accel/ivpu: Improve recovery and reset support")
Cc: stable@vger.kernel.org # v6.8+
Reviewed-by: default avatarMaciej Falkowski <maciej.falkowski@linux.intel.com>
Reviewed-by: default avatarJeffrey Hugo <quic_jhugo@quicinc.com>
Signed-off-by: default avatarJacek Lawrynowicz <jacek.lawrynowicz@linux.intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20250129124009.1039982-4-jacek.lawrynowicz@linux.intel.com
parent f2bc2afe
Loading
Loading
Loading
Loading
+43 −36
Original line number Diff line number Diff line
@@ -115,41 +115,57 @@ static int ivpu_resume(struct ivpu_device *vdev)
	return ret;
}

static void ivpu_pm_recovery_work(struct work_struct *work)
static void ivpu_pm_reset_begin(struct ivpu_device *vdev)
{
	struct ivpu_pm_info *pm = container_of(work, struct ivpu_pm_info, recovery_work);
	struct ivpu_device *vdev = pm->vdev;
	char *evt[2] = {"IVPU_PM_EVENT=IVPU_RECOVER", NULL};
	int ret;

	ivpu_err(vdev, "Recovering the NPU (reset #%d)\n", atomic_read(&vdev->pm->reset_counter));

	ret = pm_runtime_resume_and_get(vdev->drm.dev);
	if (ret)
		ivpu_err(vdev, "Failed to resume NPU: %d\n", ret);

	ivpu_jsm_state_dump(vdev);
	ivpu_dev_coredump(vdev);
	pm_runtime_disable(vdev->drm.dev);

	atomic_inc(&vdev->pm->reset_counter);
	atomic_set(&vdev->pm->reset_pending, 1);
	down_write(&vdev->pm->reset_lock);
}

static void ivpu_pm_reset_complete(struct ivpu_device *vdev)
{
	int ret;

	ivpu_suspend(vdev);
	ivpu_pm_prepare_cold_boot(vdev);
	ivpu_jobs_abort_all(vdev);
	ivpu_ms_cleanup_all(vdev);

	ret = ivpu_resume(vdev);
	if (ret)
	if (ret) {
		ivpu_err(vdev, "Failed to resume NPU: %d\n", ret);
		pm_runtime_set_suspended(vdev->drm.dev);
	} else {
		pm_runtime_set_active(vdev->drm.dev);
	}

	up_write(&vdev->pm->reset_lock);
	atomic_set(&vdev->pm->reset_pending, 0);

	kobject_uevent_env(&vdev->drm.dev->kobj, KOBJ_CHANGE, evt);
	pm_runtime_mark_last_busy(vdev->drm.dev);
	pm_runtime_put_autosuspend(vdev->drm.dev);
	pm_runtime_enable(vdev->drm.dev);
}

static void ivpu_pm_recovery_work(struct work_struct *work)
{
	struct ivpu_pm_info *pm = container_of(work, struct ivpu_pm_info, recovery_work);
	struct ivpu_device *vdev = pm->vdev;
	char *evt[2] = {"IVPU_PM_EVENT=IVPU_RECOVER", NULL};

	ivpu_err(vdev, "Recovering the NPU (reset #%d)\n", atomic_read(&vdev->pm->reset_counter));

	ivpu_pm_reset_begin(vdev);

	if (!pm_runtime_status_suspended(vdev->drm.dev)) {
		ivpu_jsm_state_dump(vdev);
		ivpu_dev_coredump(vdev);
		ivpu_suspend(vdev);
	}

	ivpu_pm_reset_complete(vdev);

	kobject_uevent_env(&vdev->drm.dev->kobj, KOBJ_CHANGE, evt);
}

void ivpu_pm_trigger_recovery(struct ivpu_device *vdev, const char *reason)
@@ -328,16 +344,13 @@ void ivpu_pm_reset_prepare_cb(struct pci_dev *pdev)
	struct ivpu_device *vdev = pci_get_drvdata(pdev);

	ivpu_dbg(vdev, PM, "Pre-reset..\n");
	atomic_inc(&vdev->pm->reset_counter);
	atomic_set(&vdev->pm->reset_pending, 1);

	pm_runtime_get_sync(vdev->drm.dev);
	down_write(&vdev->pm->reset_lock);
	ivpu_pm_reset_begin(vdev);

	if (!pm_runtime_status_suspended(vdev->drm.dev)) {
		ivpu_prepare_for_reset(vdev);
		ivpu_hw_reset(vdev);
	ivpu_pm_prepare_cold_boot(vdev);
	ivpu_jobs_abort_all(vdev);
	ivpu_ms_cleanup_all(vdev);
	}

	ivpu_dbg(vdev, PM, "Pre-reset done.\n");
}
@@ -345,18 +358,12 @@ void ivpu_pm_reset_prepare_cb(struct pci_dev *pdev)
void ivpu_pm_reset_done_cb(struct pci_dev *pdev)
{
	struct ivpu_device *vdev = pci_get_drvdata(pdev);
	int ret;

	ivpu_dbg(vdev, PM, "Post-reset..\n");
	ret = ivpu_resume(vdev);
	if (ret)
		ivpu_err(vdev, "Failed to set RESUME state: %d\n", ret);
	up_write(&vdev->pm->reset_lock);
	atomic_set(&vdev->pm->reset_pending, 0);
	ivpu_dbg(vdev, PM, "Post-reset done.\n");

	pm_runtime_mark_last_busy(vdev->drm.dev);
	pm_runtime_put_autosuspend(vdev->drm.dev);
	ivpu_pm_reset_complete(vdev);

	ivpu_dbg(vdev, PM, "Post-reset done.\n");
}

void ivpu_pm_init(struct ivpu_device *vdev)