drm/amdkfd: add sysfs counters for vm fault and migration
This is part of SVM profiling API, export sysfs counters for per-process, per-GPU vm retry fault, pages migrated in and out of GPU vram. counters will not be updated in parallel in GPU retry fault handler and migration to vram/ram path, use READ_ONCE to avoid compiler optimization. Signed-off-by: Philip Yang <Philip.Yang@amd.com> Reviewed-by: Felix Kuehling <Felix.Kuehling@amd.com> Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
This commit is contained in:
parent
dcdb4d904b
commit
751580b3ff
|
@ -730,6 +730,15 @@ struct kfd_process_device {
|
||||||
* number of CU's a device has along with number of other competing processes
|
* number of CU's a device has along with number of other competing processes
|
||||||
*/
|
*/
|
||||||
struct attribute attr_cu_occupancy;
|
struct attribute attr_cu_occupancy;
|
||||||
|
|
||||||
|
/* sysfs counters for GPU retry fault and page migration tracking */
|
||||||
|
struct kobject *kobj_counters;
|
||||||
|
struct attribute attr_faults;
|
||||||
|
struct attribute attr_page_in;
|
||||||
|
struct attribute attr_page_out;
|
||||||
|
uint64_t faults;
|
||||||
|
uint64_t page_in;
|
||||||
|
uint64_t page_out;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define qpd_to_pdd(x) container_of(x, struct kfd_process_device, qpd)
|
#define qpd_to_pdd(x) container_of(x, struct kfd_process_device, qpd)
|
||||||
|
|
|
@ -416,6 +416,29 @@ static ssize_t kfd_procfs_stats_show(struct kobject *kobj,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static ssize_t kfd_sysfs_counters_show(struct kobject *kobj,
|
||||||
|
struct attribute *attr, char *buf)
|
||||||
|
{
|
||||||
|
struct kfd_process_device *pdd;
|
||||||
|
|
||||||
|
if (!strcmp(attr->name, "faults")) {
|
||||||
|
pdd = container_of(attr, struct kfd_process_device,
|
||||||
|
attr_faults);
|
||||||
|
return sysfs_emit(buf, "%llu\n", READ_ONCE(pdd->faults));
|
||||||
|
}
|
||||||
|
if (!strcmp(attr->name, "page_in")) {
|
||||||
|
pdd = container_of(attr, struct kfd_process_device,
|
||||||
|
attr_page_in);
|
||||||
|
return sysfs_emit(buf, "%llu\n", READ_ONCE(pdd->page_in));
|
||||||
|
}
|
||||||
|
if (!strcmp(attr->name, "page_out")) {
|
||||||
|
pdd = container_of(attr, struct kfd_process_device,
|
||||||
|
attr_page_out);
|
||||||
|
return sysfs_emit(buf, "%llu\n", READ_ONCE(pdd->page_out));
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static struct attribute attr_queue_size = {
|
static struct attribute attr_queue_size = {
|
||||||
.name = "size",
|
.name = "size",
|
||||||
.mode = KFD_SYSFS_FILE_MODE
|
.mode = KFD_SYSFS_FILE_MODE
|
||||||
|
@ -456,6 +479,15 @@ static struct kobj_type procfs_stats_type = {
|
||||||
.release = kfd_procfs_kobj_release,
|
.release = kfd_procfs_kobj_release,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct sysfs_ops sysfs_counters_ops = {
|
||||||
|
.show = kfd_sysfs_counters_show,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct kobj_type sysfs_counters_type = {
|
||||||
|
.sysfs_ops = &sysfs_counters_ops,
|
||||||
|
.release = kfd_procfs_kobj_release,
|
||||||
|
};
|
||||||
|
|
||||||
int kfd_procfs_add_queue(struct queue *q)
|
int kfd_procfs_add_queue(struct queue *q)
|
||||||
{
|
{
|
||||||
struct kfd_process *proc;
|
struct kfd_process *proc;
|
||||||
|
@ -544,6 +576,50 @@ static void kfd_procfs_add_sysfs_stats(struct kfd_process *p)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void kfd_procfs_add_sysfs_counters(struct kfd_process *p)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
int i;
|
||||||
|
char counters_dir_filename[MAX_SYSFS_FILENAME_LEN];
|
||||||
|
|
||||||
|
if (!p || !p->kobj)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create sysfs files for each GPU which supports SVM
|
||||||
|
* - proc/<pid>/counters_<gpuid>/
|
||||||
|
* - proc/<pid>/counters_<gpuid>/faults
|
||||||
|
* - proc/<pid>/counters_<gpuid>/page_in
|
||||||
|
* - proc/<pid>/counters_<gpuid>/page_out
|
||||||
|
*/
|
||||||
|
for_each_set_bit(i, p->svms.bitmap_supported, p->n_pdds) {
|
||||||
|
struct kfd_process_device *pdd = p->pdds[i];
|
||||||
|
struct kobject *kobj_counters;
|
||||||
|
|
||||||
|
snprintf(counters_dir_filename, MAX_SYSFS_FILENAME_LEN,
|
||||||
|
"counters_%u", pdd->dev->id);
|
||||||
|
kobj_counters = kfd_alloc_struct(kobj_counters);
|
||||||
|
if (!kobj_counters)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ret = kobject_init_and_add(kobj_counters, &sysfs_counters_type,
|
||||||
|
p->kobj, counters_dir_filename);
|
||||||
|
if (ret) {
|
||||||
|
pr_warn("Creating KFD proc/%s folder failed",
|
||||||
|
counters_dir_filename);
|
||||||
|
kobject_put(kobj_counters);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pdd->kobj_counters = kobj_counters;
|
||||||
|
kfd_sysfs_create_file(kobj_counters, &pdd->attr_faults,
|
||||||
|
"faults");
|
||||||
|
kfd_sysfs_create_file(kobj_counters, &pdd->attr_page_in,
|
||||||
|
"page_in");
|
||||||
|
kfd_sysfs_create_file(kobj_counters, &pdd->attr_page_out,
|
||||||
|
"page_out");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void kfd_procfs_add_sysfs_files(struct kfd_process *p)
|
static void kfd_procfs_add_sysfs_files(struct kfd_process *p)
|
||||||
{
|
{
|
||||||
|
@ -777,6 +853,7 @@ struct kfd_process *kfd_create_process(struct file *filep)
|
||||||
|
|
||||||
kfd_procfs_add_sysfs_stats(process);
|
kfd_procfs_add_sysfs_stats(process);
|
||||||
kfd_procfs_add_sysfs_files(process);
|
kfd_procfs_add_sysfs_files(process);
|
||||||
|
kfd_procfs_add_sysfs_counters(process);
|
||||||
}
|
}
|
||||||
out:
|
out:
|
||||||
if (!IS_ERR(process))
|
if (!IS_ERR(process))
|
||||||
|
@ -919,6 +996,50 @@ static void kfd_process_destroy_pdds(struct kfd_process *p)
|
||||||
p->n_pdds = 0;
|
p->n_pdds = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void kfd_process_remove_sysfs(struct kfd_process *p)
|
||||||
|
{
|
||||||
|
struct kfd_process_device *pdd;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!p->kobj)
|
||||||
|
return;
|
||||||
|
|
||||||
|
sysfs_remove_file(p->kobj, &p->attr_pasid);
|
||||||
|
kobject_del(p->kobj_queues);
|
||||||
|
kobject_put(p->kobj_queues);
|
||||||
|
p->kobj_queues = NULL;
|
||||||
|
|
||||||
|
for (i = 0; i < p->n_pdds; i++) {
|
||||||
|
pdd = p->pdds[i];
|
||||||
|
|
||||||
|
sysfs_remove_file(p->kobj, &pdd->attr_vram);
|
||||||
|
sysfs_remove_file(p->kobj, &pdd->attr_sdma);
|
||||||
|
|
||||||
|
sysfs_remove_file(pdd->kobj_stats, &pdd->attr_evict);
|
||||||
|
if (pdd->dev->kfd2kgd->get_cu_occupancy)
|
||||||
|
sysfs_remove_file(pdd->kobj_stats,
|
||||||
|
&pdd->attr_cu_occupancy);
|
||||||
|
kobject_del(pdd->kobj_stats);
|
||||||
|
kobject_put(pdd->kobj_stats);
|
||||||
|
pdd->kobj_stats = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
for_each_set_bit(i, p->svms.bitmap_supported, p->n_pdds) {
|
||||||
|
pdd = p->pdds[i];
|
||||||
|
|
||||||
|
sysfs_remove_file(pdd->kobj_counters, &pdd->attr_faults);
|
||||||
|
sysfs_remove_file(pdd->kobj_counters, &pdd->attr_page_in);
|
||||||
|
sysfs_remove_file(pdd->kobj_counters, &pdd->attr_page_out);
|
||||||
|
kobject_del(pdd->kobj_counters);
|
||||||
|
kobject_put(pdd->kobj_counters);
|
||||||
|
pdd->kobj_counters = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
kobject_del(p->kobj);
|
||||||
|
kobject_put(p->kobj);
|
||||||
|
p->kobj = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/* No process locking is needed in this function, because the process
|
/* No process locking is needed in this function, because the process
|
||||||
* is not findable any more. We must assume that no other thread is
|
* is not findable any more. We must assume that no other thread is
|
||||||
* using it any more, otherwise we couldn't safely free the process
|
* using it any more, otherwise we couldn't safely free the process
|
||||||
|
@ -928,35 +1049,7 @@ static void kfd_process_wq_release(struct work_struct *work)
|
||||||
{
|
{
|
||||||
struct kfd_process *p = container_of(work, struct kfd_process,
|
struct kfd_process *p = container_of(work, struct kfd_process,
|
||||||
release_work);
|
release_work);
|
||||||
int i;
|
kfd_process_remove_sysfs(p);
|
||||||
|
|
||||||
/* Remove the procfs files */
|
|
||||||
if (p->kobj) {
|
|
||||||
sysfs_remove_file(p->kobj, &p->attr_pasid);
|
|
||||||
kobject_del(p->kobj_queues);
|
|
||||||
kobject_put(p->kobj_queues);
|
|
||||||
p->kobj_queues = NULL;
|
|
||||||
|
|
||||||
for (i = 0; i < p->n_pdds; i++) {
|
|
||||||
struct kfd_process_device *pdd = p->pdds[i];
|
|
||||||
|
|
||||||
sysfs_remove_file(p->kobj, &pdd->attr_vram);
|
|
||||||
sysfs_remove_file(p->kobj, &pdd->attr_sdma);
|
|
||||||
|
|
||||||
sysfs_remove_file(pdd->kobj_stats, &pdd->attr_evict);
|
|
||||||
if (pdd->dev->kfd2kgd->get_cu_occupancy)
|
|
||||||
sysfs_remove_file(pdd->kobj_stats,
|
|
||||||
&pdd->attr_cu_occupancy);
|
|
||||||
kobject_del(pdd->kobj_stats);
|
|
||||||
kobject_put(pdd->kobj_stats);
|
|
||||||
pdd->kobj_stats = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
kobject_del(p->kobj);
|
|
||||||
kobject_put(p->kobj);
|
|
||||||
p->kobj = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
kfd_iommu_unbind_process(p);
|
kfd_iommu_unbind_process(p);
|
||||||
|
|
||||||
kfd_process_free_outstanding_kfd_bos(p);
|
kfd_process_free_outstanding_kfd_bos(p);
|
||||||
|
|
Loading…
Reference in New Issue