Commit 0adff3b0 authored by Tomasz Rusinowicz's avatar Tomasz Rusinowicz Committed by Jacek Lawrynowicz

accel/ivpu: Share NPU busy time in sysfs

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: default avatarTomasz Rusinowicz <tomasz.rusinowicz@intel.com>
Signed-off-by: default avatarJacek Lawrynowicz <jacek.lawrynowicz@linux.intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20240513120431.3187212-13-jacek.lawrynowicz@linux.intel.com
parent cb323de3
...@@ -14,7 +14,8 @@ intel_vpu-y := \ ...@@ -14,7 +14,8 @@ intel_vpu-y := \
ivpu_mmu.o \ ivpu_mmu.o \
ivpu_mmu_context.o \ ivpu_mmu_context.o \
ivpu_ms.o \ ivpu_ms.o \
ivpu_pm.o ivpu_pm.o \
ivpu_sysfs.o
intel_vpu-$(CONFIG_DEBUG_FS) += ivpu_debugfs.o intel_vpu-$(CONFIG_DEBUG_FS) += ivpu_debugfs.o
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include "ivpu_mmu_context.h" #include "ivpu_mmu_context.h"
#include "ivpu_ms.h" #include "ivpu_ms.h"
#include "ivpu_pm.h" #include "ivpu_pm.h"
#include "ivpu_sysfs.h"
#ifndef DRIVER_VERSION_STR #ifndef DRIVER_VERSION_STR
#define DRIVER_VERSION_STR __stringify(DRM_IVPU_DRIVER_MAJOR) "." \ #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) ...@@ -696,6 +697,7 @@ static int ivpu_probe(struct pci_dev *pdev, const struct pci_device_id *id)
return ret; return ret;
ivpu_debugfs_init(vdev); ivpu_debugfs_init(vdev);
ivpu_sysfs_init(vdev);
ret = drm_dev_register(&vdev->drm, 0); ret = drm_dev_register(&vdev->drm, 0);
if (ret) { if (ret) {
......
...@@ -135,6 +135,9 @@ struct ivpu_device { ...@@ -135,6 +135,9 @@ struct ivpu_device {
atomic64_t unique_id_counter; atomic64_t unique_id_counter;
ktime_t busy_start_ts;
ktime_t busy_time;
struct { struct {
int boot; int boot;
int jsm; int jsm;
......
...@@ -438,11 +438,28 @@ ivpu_job_create(struct ivpu_file_priv *file_priv, u32 engine_idx, u32 bo_count) ...@@ -438,11 +438,28 @@ ivpu_job_create(struct ivpu_file_priv *file_priv, u32 engine_idx, u32 bo_count)
return NULL; 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) static int ivpu_job_signal_and_destroy(struct ivpu_device *vdev, u32 job_id, u32 job_status)
{ {
struct ivpu_job *job; 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) if (!job)
return -ENOENT; return -ENOENT;
...@@ -477,6 +494,7 @@ static int ivpu_job_submit(struct ivpu_job *job, u8 priority) ...@@ -477,6 +494,7 @@ static int ivpu_job_submit(struct ivpu_job *job, u8 priority)
struct ivpu_device *vdev = job->vdev; struct ivpu_device *vdev = job->vdev;
struct xa_limit job_id_range; struct xa_limit job_id_range;
struct ivpu_cmdq *cmdq; struct ivpu_cmdq *cmdq;
bool is_first_job;
int ret; int ret;
ret = ivpu_rpm_get(vdev); ret = ivpu_rpm_get(vdev);
...@@ -497,6 +515,7 @@ static int ivpu_job_submit(struct ivpu_job *job, u8 priority) ...@@ -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; job_id_range.max = job_id_range.min | JOB_ID_JOB_MASK;
xa_lock(&vdev->submitted_jobs_xa); 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); ret = __xa_alloc(&vdev->submitted_jobs_xa, &job->job_id, job, job_id_range, GFP_KERNEL);
if (ret) { if (ret) {
ivpu_dbg(vdev, JOB, "Too many active jobs in ctx %d\n", 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) ...@@ -516,6 +535,8 @@ static int ivpu_job_submit(struct ivpu_job *job, u8 priority)
wmb(); /* Flush WC buffer for jobq header */ wmb(); /* Flush WC buffer for jobq header */
} else { } else {
ivpu_cmdq_ring_db(vdev, cmdq); 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", ivpu_dbg(vdev, JOB, "Job submitted: id %3u ctx %2d engine %d prio %d addr 0x%llx next %d\n",
......
// 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);
}
/* 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__ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment