]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
accel/ivpu: Share NPU busy time in sysfs
authorTomasz Rusinowicz <tomasz.rusinowicz@intel.com>
Mon, 13 May 2024 12:04:31 +0000 (14:04 +0200)
committerJacek Lawrynowicz <jacek.lawrynowicz@linux.intel.com>
Wed, 15 May 2024 05:42:29 +0000 (07:42 +0200)
The driver tracks the time spent by NPU executing jobs
and shares it through sysfs `npu_busy_time_us` file.
It can be then used by user space applications to monitor device
utilization.

NPU is considered 'busy' starting with a first job submitted
to firmware and ending when there is no more jobs pending/executing.

Signed-off-by: Tomasz Rusinowicz <tomasz.rusinowicz@intel.com>
Signed-off-by: Jacek Lawrynowicz <jacek.lawrynowicz@linux.intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20240513120431.3187212-13-jacek.lawrynowicz@linux.intel.com
drivers/accel/ivpu/Makefile
drivers/accel/ivpu/ivpu_drv.c
drivers/accel/ivpu/ivpu_drv.h
drivers/accel/ivpu/ivpu_job.c
drivers/accel/ivpu/ivpu_sysfs.c [new file with mode: 0644]
drivers/accel/ivpu/ivpu_sysfs.h [new file with mode: 0644]

index 1c67a73cfefef1b84a6e2ff7e30ea890044a3b64..e16a9f5c1c8914912e38c5ce8bc693ae023d6ca2 100644 (file)
@@ -14,7 +14,8 @@ intel_vpu-y := \
        ivpu_mmu.o \
        ivpu_mmu_context.o \
        ivpu_ms.o \
-       ivpu_pm.o
+       ivpu_pm.o \
+       ivpu_sysfs.o
 
 intel_vpu-$(CONFIG_DEBUG_FS) += ivpu_debugfs.o
 
index bd702401216c31e2a0954e0c02424e2578258d87..130455d398410730ec23fd441969ac60418ee0d4 100644 (file)
@@ -28,6 +28,7 @@
 #include "ivpu_mmu_context.h"
 #include "ivpu_ms.h"
 #include "ivpu_pm.h"
+#include "ivpu_sysfs.h"
 
 #ifndef DRIVER_VERSION_STR
 #define DRIVER_VERSION_STR __stringify(DRM_IVPU_DRIVER_MAJOR) "." \
@@ -696,6 +697,7 @@ static int ivpu_probe(struct pci_dev *pdev, const struct pci_device_id *id)
                return ret;
 
        ivpu_debugfs_init(vdev);
+       ivpu_sysfs_init(vdev);
 
        ret = drm_dev_register(&vdev->drm, 0);
        if (ret) {
index 973f8ded23e9a9c5c31d526c45f1293607aba985..4de7fc0c702619f7070e9c843b2c4ea580860c94 100644 (file)
@@ -135,6 +135,9 @@ struct ivpu_device {
 
        atomic64_t unique_id_counter;
 
+       ktime_t busy_start_ts;
+       ktime_t busy_time;
+
        struct {
                int boot;
                int jsm;
index 1d7b4388eb3bf6a7f12ebfdacd63ad91c1f84488..845181b48b3a47c3bf8af3b92a7b7c7ca0e4a670 100644 (file)
@@ -438,11 +438,28 @@ err_free_job:
        return NULL;
 }
 
+static struct ivpu_job *ivpu_job_remove_from_submitted_jobs(struct ivpu_device *vdev, u32 job_id)
+{
+       struct ivpu_job *job;
+
+       xa_lock(&vdev->submitted_jobs_xa);
+       job = __xa_erase(&vdev->submitted_jobs_xa, job_id);
+
+       if (xa_empty(&vdev->submitted_jobs_xa) && job) {
+               vdev->busy_time = ktime_add(ktime_sub(ktime_get(), vdev->busy_start_ts),
+                                           vdev->busy_time);
+       }
+
+       xa_unlock(&vdev->submitted_jobs_xa);
+
+       return job;
+}
+
 static int ivpu_job_signal_and_destroy(struct ivpu_device *vdev, u32 job_id, u32 job_status)
 {
        struct ivpu_job *job;
 
-       job = xa_erase(&vdev->submitted_jobs_xa, job_id);
+       job = ivpu_job_remove_from_submitted_jobs(vdev, job_id);
        if (!job)
                return -ENOENT;
 
@@ -477,6 +494,7 @@ static int ivpu_job_submit(struct ivpu_job *job, u8 priority)
        struct ivpu_device *vdev = job->vdev;
        struct xa_limit job_id_range;
        struct ivpu_cmdq *cmdq;
+       bool is_first_job;
        int ret;
 
        ret = ivpu_rpm_get(vdev);
@@ -497,6 +515,7 @@ static int ivpu_job_submit(struct ivpu_job *job, u8 priority)
        job_id_range.max = job_id_range.min | JOB_ID_JOB_MASK;
 
        xa_lock(&vdev->submitted_jobs_xa);
+       is_first_job = xa_empty(&vdev->submitted_jobs_xa);
        ret = __xa_alloc(&vdev->submitted_jobs_xa, &job->job_id, job, job_id_range, GFP_KERNEL);
        if (ret) {
                ivpu_dbg(vdev, JOB, "Too many active jobs in ctx %d\n",
@@ -516,6 +535,8 @@ static int ivpu_job_submit(struct ivpu_job *job, u8 priority)
                wmb(); /* Flush WC buffer for jobq header */
        } else {
                ivpu_cmdq_ring_db(vdev, cmdq);
+               if (is_first_job)
+                       vdev->busy_start_ts = ktime_get();
        }
 
        ivpu_dbg(vdev, JOB, "Job submitted: id %3u ctx %2d engine %d prio %d addr 0x%llx next %d\n",
diff --git a/drivers/accel/ivpu/ivpu_sysfs.c b/drivers/accel/ivpu/ivpu_sysfs.c
new file mode 100644 (file)
index 0000000..913669f
--- /dev/null
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2024 Intel Corporation
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+
+#include "ivpu_hw.h"
+#include "ivpu_sysfs.h"
+
+/*
+ * npu_busy_time_us is the time that the device spent executing jobs.
+ * The time is counted when and only when there are jobs submitted to firmware.
+ *
+ * This time can be used to measure the utilization of NPU, either by calculating
+ * npu_busy_time_us difference between two timepoints (i.e. measuring the time
+ * that the NPU was active during some workload) or monitoring utilization percentage
+ * by reading npu_busy_time_us periodically.
+ *
+ * When reading the value periodically, it shouldn't be read too often as it may have
+ * an impact on job submission performance. Recommended period is 1 second.
+ */
+static ssize_t
+npu_busy_time_us_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct drm_device *drm = dev_get_drvdata(dev);
+       struct ivpu_device *vdev = to_ivpu_device(drm);
+       ktime_t total, now = 0;
+
+       xa_lock(&vdev->submitted_jobs_xa);
+       total = vdev->busy_time;
+       if (!xa_empty(&vdev->submitted_jobs_xa))
+               now = ktime_sub(ktime_get(), vdev->busy_start_ts);
+       xa_unlock(&vdev->submitted_jobs_xa);
+
+       return sysfs_emit(buf, "%lld\n", ktime_to_us(ktime_add(total, now)));
+}
+
+static DEVICE_ATTR_RO(npu_busy_time_us);
+
+static struct attribute *ivpu_dev_attrs[] = {
+       &dev_attr_npu_busy_time_us.attr,
+       NULL,
+};
+
+static struct attribute_group ivpu_dev_attr_group = {
+       .attrs = ivpu_dev_attrs,
+};
+
+void ivpu_sysfs_init(struct ivpu_device *vdev)
+{
+       int ret;
+
+       ret = devm_device_add_group(vdev->drm.dev, &ivpu_dev_attr_group);
+       if (ret)
+               ivpu_warn(vdev, "Failed to add group to device, ret %d", ret);
+}
diff --git a/drivers/accel/ivpu/ivpu_sysfs.h b/drivers/accel/ivpu/ivpu_sysfs.h
new file mode 100644 (file)
index 0000000..9836f09
--- /dev/null
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2024 Intel Corporation
+ */
+
+#ifndef __IVPU_SYSFS_H__
+#define __IVPU_SYSFS_H__
+
+#include "ivpu_drv.h"
+
+void ivpu_sysfs_init(struct ivpu_device *vdev);
+
+#endif /* __IVPU_SYSFS_H__ */