[Why]
Allow user to know how many compute units (CU) are in use at any given
moment.

[How]
Surface files in Sysfs that allow user to determine the number of compute
units that are in use for a given process. One Sysfs file is used per
device.

Signed-off-by: Ramesh Errabolu <ramesh.errab...@amd.com>
---
 drivers/gpu/drm/amd/amdkfd/kfd_priv.h    |  28 ++++++-
 drivers/gpu/drm/amd/amdkfd/kfd_process.c | 101 +++++++++++++++++++----
 2 files changed, 114 insertions(+), 15 deletions(-)

diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h 
b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h
index 023629f28495..2ce03586d0dc 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h
@@ -631,7 +631,7 @@ enum kfd_pdd_bound {
        PDD_BOUND_SUSPENDED,
 };
 
-#define MAX_SYSFS_FILENAME_LEN 11
+#define MAX_SYSFS_FILENAME_LEN 32
 
 /*
  * SDMA counter runs at 100MHz frequency.
@@ -692,6 +692,32 @@ struct kfd_process_device {
        uint64_t sdma_past_activity_counter;
        struct attribute attr_sdma;
        char sdma_filename[MAX_SYSFS_FILENAME_LEN];
+
+       /*
+        * @cu_occupancy: Reports occupancy of Compute Units (CU) of a process
+        * that is associated with device encoded by "this" struct instance. The
+        * value reflects CU usage by all of the waves launched by this process
+        * on this device. A very important property of occupancy parameter is
+        * that its value is a a snapshot of current use.
+        *
+        * Following is to be noted regarding how this parameter is reported:
+        *
+        *  The number of waves that a CU can launch is limited by couple of
+        *  parameters. These are encoded by struct amdgpu_cu_info instance
+        *  that is part of every device definition. For GFX9 devices this
+        *  translates to 40 waves (simd_per_cu * max_waves_per_simd) when waves
+        *  do not use scratch memory and 32 waves (max_scratch_slots_per_cu)
+        *  when they use. This could change for future devices and therefore
+        *  this example should be considered as a guide.
+        *
+        *  All CU's of a device are available for the process. This may not be 
true
+        *  under certain conditions - e.g. CU masking.
+        *
+        *  Finally number of CU's that are occupied by a process is affected 
by both
+        *  number of CU's a device has along with number of other competing 
processes
+        */
+       struct attribute attr_cu_occupancy;
+       char cu_occupancy_filename[MAX_SYSFS_FILENAME_LEN];
 };
 
 #define qpd_to_pdd(x) container_of(x, struct kfd_process_device, qpd)
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_process.c 
b/drivers/gpu/drm/amd/amdkfd/kfd_process.c
index a0e12a79ab7d..3baffbc828b2 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_process.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_process.c
@@ -249,6 +249,63 @@ static void kfd_sdma_activity_worker(struct work_struct 
*work)
        }
 }
 
+/**
+ * @kfd_get_cu_occupancy() - Collect number of waves in-flight on this device
+ * by current process. Translates acquired wave count into number of compute 
units
+ * that are occupied.
+ *
+ * @atr: Handle of attribute that allows reporting of wave count. The attribute
+ * handle encapsulates GPU device it is associated with, thereby allowing 
collection
+ * of waves in flight, etc
+ *
+ * @buffer: Handle of user provided buffer updated with wave count
+ *
+ * Return: Number of bytes written to user buffer or an error value
+ */
+static int kfd_get_cu_occupancy(struct attribute *attr, char *buffer)
+{
+       int cu_cnt;
+       int wave_cnt;
+       int max_waves_per_cu;
+       struct kfd_dev *dev = NULL;
+       struct kfd_process *proc = NULL;
+       struct amdgpu_device *adev = NULL;
+       struct kfd_process_device *pdd = NULL;
+
+       /* Acquire handle of Process-Device-Data associated with attribute */
+       pdd = container_of(attr, struct kfd_process_device, attr_cu_occupancy);
+
+       /*
+        * Acquire handle of Gpu Device associated with attribute. Determine
+        * if ability to collect CU occupancy is defined for this device. If
+        * so acquire the handle of process which encapsulates the PASID of
+        * process. It is possible that the process might have zero work on
+        * device. This is determined by checking if process has any queues
+        * @note: Ability to collect is defined only for Gfx9 devices
+        */
+       cu_cnt = 0;
+       dev = pdd->dev;
+       proc = pdd->process;
+       if (pdd->qpd.queue_count == 0) {
+               pr_info("%s: Gpu-Id: %d has no active queues for process %d\n",
+                       __func__, dev->id, proc->pasid);
+               return snprintf(buffer, PAGE_SIZE, "%d\n", cu_cnt);
+       }
+
+       /* Collect wave count from device by reading relevant registers */
+       wave_cnt = 0;
+       if (dev->kfd2kgd->get_cu_occupancy != NULL)
+               dev->kfd2kgd->get_cu_occupancy(dev->kgd, proc->pasid,
+                                              &wave_cnt);
+
+       /* Translate wave count to number of compute units */
+       adev = (struct amdgpu_device *)dev->kgd;
+       max_waves_per_cu = adev->gfx.cu_info.simd_per_cu *
+               adev->gfx.cu_info.max_waves_per_simd;
+       cu_cnt = wave_cnt / max_waves_per_cu;
+       return snprintf(buffer, PAGE_SIZE, "%d\n", cu_cnt);
+}
+
 static ssize_t kfd_procfs_show(struct kobject *kobj, struct attribute *attr,
                               char *buffer)
 {
@@ -279,6 +336,10 @@ static ssize_t kfd_procfs_show(struct kobject *kobj, 
struct attribute *attr,
                return snprintf(buffer, PAGE_SIZE, "%llu\n",
                                
(sdma_activity_work_handler.sdma_activity_counter)/
                                 SDMA_ACTIVITY_DIVISOR);
+
+       /* Sysfs handle that gets CU occupancy is per device */
+       } else if (strncmp(attr->name, "cu_occupancy_", 13) == 0) {
+               return kfd_get_cu_occupancy(attr, buffer);
        } else {
                pr_err("Invalid attribute");
                return -EINVAL;
@@ -432,27 +493,37 @@ static int kfd_procfs_add_sysfs_files(struct kfd_process 
*p)
         * Create sysfs files for each GPU:
         * - proc/<pid>/vram_<gpuid>
         * - proc/<pid>/sdma_<gpuid>
+        * - proc/<pid>/cu_occupancy_<gpuid>
         */
        list_for_each_entry(pdd, &p->per_device_data, per_device_list) {
                snprintf(pdd->vram_filename, MAX_SYSFS_FILENAME_LEN, "vram_%u",
                         pdd->dev->id);
-               ret = kfd_sysfs_create_file(p, &pdd->attr_vram, 
pdd->vram_filename);
+               ret = kfd_sysfs_create_file(p, &pdd->attr_vram,
+                                           pdd->vram_filename);
                if (ret)
                        pr_warn("Creating vram usage for gpu id %d failed",
                                (int)pdd->dev->id);
-
-               snprintf(pdd->sdma_filename, MAX_SYSFS_FILENAME_LEN, "sdma_%u",
-                        pdd->dev->id);
-               ret = kfd_sysfs_create_file(p, &pdd->attr_sdma, 
pdd->sdma_filename);
-               if (ret)
-                       pr_warn("Creating sdma usage for gpu id %d failed",
-                               (int)pdd->dev->id);
-       }
-
+               if (pdd->dev->kfd2kgd->get_cu_occupancy != NULL) {
+                       snprintf(pdd->sdma_filename, MAX_SYSFS_FILENAME_LEN,
+                                "sdma_%u", pdd->dev->id);
+                       ret = kfd_sysfs_create_file(p, &pdd->attr_sdma,
+                                                   pdd->sdma_filename);
+                       if (ret)
+                               pr_warn("Creating sdma usage for gpu id %d 
failed",
+                                       (int)pdd->dev->id);
+                       snprintf(pdd->cu_occupancy_filename,
+                                MAX_SYSFS_FILENAME_LEN, "cu_occupancy_%u",
+                                pdd->dev->id);
+                       ret = kfd_sysfs_create_file(p, &pdd->attr_cu_occupancy,
+                                                   pdd->cu_occupancy_filename);
+                       if (ret)
+                               pr_warn("Creating CU occupancy file for gpu id 
%d failed",
+                                       (int)pdd->dev->id);
+                       }
+               }
        return ret;
 }
 
-
 void kfd_procfs_del_queue(struct queue *q)
 {
        if (!q)
@@ -767,8 +838,7 @@ static void kfd_process_destroy_pdds(struct kfd_process *p)
                        amdgpu_amdkfd_gpuvm_release_process_vm(
                                        pdd->dev->kgd, pdd->vm);
                        fput(pdd->drm_file);
-               }
-               else if (pdd->vm)
+               } else if (pdd->vm)
                        amdgpu_amdkfd_gpuvm_destroy_process_vm(
                                pdd->dev->kgd, pdd->vm);
 
@@ -815,7 +885,10 @@ static void kfd_process_wq_release(struct work_struct 
*work)
 
                list_for_each_entry(pdd, &p->per_device_data, per_device_list) {
                        sysfs_remove_file(p->kobj, &pdd->attr_vram);
-                       sysfs_remove_file(p->kobj, &pdd->attr_sdma);
+                       if (pdd->dev->kfd2kgd->get_cu_occupancy != NULL) {
+                               sysfs_remove_file(p->kobj, &pdd->attr_sdma);
+                               sysfs_remove_file(p->kobj, 
&pdd->attr_cu_occupancy);
+                       }
                }
 
                kobject_del(p->kobj);
-- 
2.27.0

_______________________________________________
amd-gfx mailing list
amd-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/amd-gfx

Reply via email to