18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright 2014 Advanced Micro Devices, Inc.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
58c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the "Software"),
68c2ecf20Sopenharmony_ci * to deal in the Software without restriction, including without limitation
78c2ecf20Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense,
88c2ecf20Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
98c2ecf20Sopenharmony_ci * Software is furnished to do so, subject to the following conditions:
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice shall be included in
128c2ecf20Sopenharmony_ci * all copies or substantial portions of the Software.
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
158c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
168c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
178c2ecf20Sopenharmony_ci * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
188c2ecf20Sopenharmony_ci * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
198c2ecf20Sopenharmony_ci * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
208c2ecf20Sopenharmony_ci * OTHER DEALINGS IN THE SOFTWARE.
218c2ecf20Sopenharmony_ci */
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include <linux/mutex.h>
248c2ecf20Sopenharmony_ci#include <linux/log2.h>
258c2ecf20Sopenharmony_ci#include <linux/sched.h>
268c2ecf20Sopenharmony_ci#include <linux/sched/mm.h>
278c2ecf20Sopenharmony_ci#include <linux/sched/task.h>
288c2ecf20Sopenharmony_ci#include <linux/mmu_context.h>
298c2ecf20Sopenharmony_ci#include <linux/slab.h>
308c2ecf20Sopenharmony_ci#include <linux/amd-iommu.h>
318c2ecf20Sopenharmony_ci#include <linux/notifier.h>
328c2ecf20Sopenharmony_ci#include <linux/compat.h>
338c2ecf20Sopenharmony_ci#include <linux/mman.h>
348c2ecf20Sopenharmony_ci#include <linux/file.h>
358c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h>
368c2ecf20Sopenharmony_ci#include "amdgpu_amdkfd.h"
378c2ecf20Sopenharmony_ci#include "amdgpu.h"
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistruct mm_struct;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci#include "kfd_priv.h"
428c2ecf20Sopenharmony_ci#include "kfd_device_queue_manager.h"
438c2ecf20Sopenharmony_ci#include "kfd_dbgmgr.h"
448c2ecf20Sopenharmony_ci#include "kfd_iommu.h"
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci/*
478c2ecf20Sopenharmony_ci * List of struct kfd_process (field kfd_process).
488c2ecf20Sopenharmony_ci * Unique/indexed by mm_struct*
498c2ecf20Sopenharmony_ci */
508c2ecf20Sopenharmony_ciDEFINE_HASHTABLE(kfd_processes_table, KFD_PROCESS_TABLE_SIZE);
518c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(kfd_processes_mutex);
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ciDEFINE_SRCU(kfd_processes_srcu);
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci/* For process termination handling */
568c2ecf20Sopenharmony_cistatic struct workqueue_struct *kfd_process_wq;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci/* Ordered, single-threaded workqueue for restoring evicted
598c2ecf20Sopenharmony_ci * processes. Restoring multiple processes concurrently under memory
608c2ecf20Sopenharmony_ci * pressure can lead to processes blocking each other from validating
618c2ecf20Sopenharmony_ci * their BOs and result in a live-lock situation where processes
628c2ecf20Sopenharmony_ci * remain evicted indefinitely.
638c2ecf20Sopenharmony_ci */
648c2ecf20Sopenharmony_cistatic struct workqueue_struct *kfd_restore_wq;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_cistatic struct kfd_process *find_process(const struct task_struct *thread);
678c2ecf20Sopenharmony_cistatic void kfd_process_ref_release(struct kref *ref);
688c2ecf20Sopenharmony_cistatic struct kfd_process *create_process(const struct task_struct *thread);
698c2ecf20Sopenharmony_cistatic int kfd_process_init_cwsr_apu(struct kfd_process *p, struct file *filep);
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic void evict_process_worker(struct work_struct *work);
728c2ecf20Sopenharmony_cistatic void restore_process_worker(struct work_struct *work);
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cistruct kfd_procfs_tree {
758c2ecf20Sopenharmony_ci	struct kobject *kobj;
768c2ecf20Sopenharmony_ci};
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_cistatic struct kfd_procfs_tree procfs;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci/*
818c2ecf20Sopenharmony_ci * Structure for SDMA activity tracking
828c2ecf20Sopenharmony_ci */
838c2ecf20Sopenharmony_cistruct kfd_sdma_activity_handler_workarea {
848c2ecf20Sopenharmony_ci	struct work_struct sdma_activity_work;
858c2ecf20Sopenharmony_ci	struct kfd_process_device *pdd;
868c2ecf20Sopenharmony_ci	uint64_t sdma_activity_counter;
878c2ecf20Sopenharmony_ci};
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_cistruct temp_sdma_queue_list {
908c2ecf20Sopenharmony_ci	uint64_t __user *rptr;
918c2ecf20Sopenharmony_ci	uint64_t sdma_val;
928c2ecf20Sopenharmony_ci	unsigned int queue_id;
938c2ecf20Sopenharmony_ci	struct list_head list;
948c2ecf20Sopenharmony_ci};
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_cistatic void kfd_sdma_activity_worker(struct work_struct *work)
978c2ecf20Sopenharmony_ci{
988c2ecf20Sopenharmony_ci	struct kfd_sdma_activity_handler_workarea *workarea;
998c2ecf20Sopenharmony_ci	struct kfd_process_device *pdd;
1008c2ecf20Sopenharmony_ci	uint64_t val;
1018c2ecf20Sopenharmony_ci	struct mm_struct *mm;
1028c2ecf20Sopenharmony_ci	struct queue *q;
1038c2ecf20Sopenharmony_ci	struct qcm_process_device *qpd;
1048c2ecf20Sopenharmony_ci	struct device_queue_manager *dqm;
1058c2ecf20Sopenharmony_ci	int ret = 0;
1068c2ecf20Sopenharmony_ci	struct temp_sdma_queue_list sdma_q_list;
1078c2ecf20Sopenharmony_ci	struct temp_sdma_queue_list *sdma_q, *next;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	workarea = container_of(work, struct kfd_sdma_activity_handler_workarea,
1108c2ecf20Sopenharmony_ci				sdma_activity_work);
1118c2ecf20Sopenharmony_ci	if (!workarea)
1128c2ecf20Sopenharmony_ci		return;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	pdd = workarea->pdd;
1158c2ecf20Sopenharmony_ci	if (!pdd)
1168c2ecf20Sopenharmony_ci		return;
1178c2ecf20Sopenharmony_ci	dqm = pdd->dev->dqm;
1188c2ecf20Sopenharmony_ci	qpd = &pdd->qpd;
1198c2ecf20Sopenharmony_ci	if (!dqm || !qpd)
1208c2ecf20Sopenharmony_ci		return;
1218c2ecf20Sopenharmony_ci	/*
1228c2ecf20Sopenharmony_ci	 * Total SDMA activity is current SDMA activity + past SDMA activity
1238c2ecf20Sopenharmony_ci	 * Past SDMA count is stored in pdd.
1248c2ecf20Sopenharmony_ci	 * To get the current activity counters for all active SDMA queues,
1258c2ecf20Sopenharmony_ci	 * we loop over all SDMA queues and get their counts from user-space.
1268c2ecf20Sopenharmony_ci	 *
1278c2ecf20Sopenharmony_ci	 * We cannot call get_user() with dqm_lock held as it can cause
1288c2ecf20Sopenharmony_ci	 * a circular lock dependency situation. To read the SDMA stats,
1298c2ecf20Sopenharmony_ci	 * we need to do the following:
1308c2ecf20Sopenharmony_ci	 *
1318c2ecf20Sopenharmony_ci	 * 1. Create a temporary list of SDMA queue nodes from the qpd->queues_list,
1328c2ecf20Sopenharmony_ci	 *    with dqm_lock/dqm_unlock().
1338c2ecf20Sopenharmony_ci	 * 2. Call get_user() for each node in temporary list without dqm_lock.
1348c2ecf20Sopenharmony_ci	 *    Save the SDMA count for each node and also add the count to the total
1358c2ecf20Sopenharmony_ci	 *    SDMA count counter.
1368c2ecf20Sopenharmony_ci	 *    Its possible, during this step, a few SDMA queue nodes got deleted
1378c2ecf20Sopenharmony_ci	 *    from the qpd->queues_list.
1388c2ecf20Sopenharmony_ci	 * 3. Do a second pass over qpd->queues_list to check if any nodes got deleted.
1398c2ecf20Sopenharmony_ci	 *    If any node got deleted, its SDMA count would be captured in the sdma
1408c2ecf20Sopenharmony_ci	 *    past activity counter. So subtract the SDMA counter stored in step 2
1418c2ecf20Sopenharmony_ci	 *    for this node from the total SDMA count.
1428c2ecf20Sopenharmony_ci	 */
1438c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&sdma_q_list.list);
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	/*
1468c2ecf20Sopenharmony_ci	 * Create the temp list of all SDMA queues
1478c2ecf20Sopenharmony_ci	 */
1488c2ecf20Sopenharmony_ci	dqm_lock(dqm);
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	list_for_each_entry(q, &qpd->queues_list, list) {
1518c2ecf20Sopenharmony_ci		if ((q->properties.type != KFD_QUEUE_TYPE_SDMA) &&
1528c2ecf20Sopenharmony_ci		    (q->properties.type != KFD_QUEUE_TYPE_SDMA_XGMI))
1538c2ecf20Sopenharmony_ci			continue;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci		sdma_q = kzalloc(sizeof(struct temp_sdma_queue_list), GFP_KERNEL);
1568c2ecf20Sopenharmony_ci		if (!sdma_q) {
1578c2ecf20Sopenharmony_ci			dqm_unlock(dqm);
1588c2ecf20Sopenharmony_ci			goto cleanup;
1598c2ecf20Sopenharmony_ci		}
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&sdma_q->list);
1628c2ecf20Sopenharmony_ci		sdma_q->rptr = (uint64_t __user *)q->properties.read_ptr;
1638c2ecf20Sopenharmony_ci		sdma_q->queue_id = q->properties.queue_id;
1648c2ecf20Sopenharmony_ci		list_add_tail(&sdma_q->list, &sdma_q_list.list);
1658c2ecf20Sopenharmony_ci	}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	/*
1688c2ecf20Sopenharmony_ci	 * If the temp list is empty, then no SDMA queues nodes were found in
1698c2ecf20Sopenharmony_ci	 * qpd->queues_list. Return the past activity count as the total sdma
1708c2ecf20Sopenharmony_ci	 * count
1718c2ecf20Sopenharmony_ci	 */
1728c2ecf20Sopenharmony_ci	if (list_empty(&sdma_q_list.list)) {
1738c2ecf20Sopenharmony_ci		workarea->sdma_activity_counter = pdd->sdma_past_activity_counter;
1748c2ecf20Sopenharmony_ci		dqm_unlock(dqm);
1758c2ecf20Sopenharmony_ci		return;
1768c2ecf20Sopenharmony_ci	}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	dqm_unlock(dqm);
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	/*
1818c2ecf20Sopenharmony_ci	 * Get the usage count for each SDMA queue in temp_list.
1828c2ecf20Sopenharmony_ci	 */
1838c2ecf20Sopenharmony_ci	mm = get_task_mm(pdd->process->lead_thread);
1848c2ecf20Sopenharmony_ci	if (!mm)
1858c2ecf20Sopenharmony_ci		goto cleanup;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	kthread_use_mm(mm);
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	list_for_each_entry(sdma_q, &sdma_q_list.list, list) {
1908c2ecf20Sopenharmony_ci		val = 0;
1918c2ecf20Sopenharmony_ci		ret = read_sdma_queue_counter(sdma_q->rptr, &val);
1928c2ecf20Sopenharmony_ci		if (ret) {
1938c2ecf20Sopenharmony_ci			pr_debug("Failed to read SDMA queue active counter for queue id: %d",
1948c2ecf20Sopenharmony_ci				 sdma_q->queue_id);
1958c2ecf20Sopenharmony_ci		} else {
1968c2ecf20Sopenharmony_ci			sdma_q->sdma_val = val;
1978c2ecf20Sopenharmony_ci			workarea->sdma_activity_counter += val;
1988c2ecf20Sopenharmony_ci		}
1998c2ecf20Sopenharmony_ci	}
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	kthread_unuse_mm(mm);
2028c2ecf20Sopenharmony_ci	mmput(mm);
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	/*
2058c2ecf20Sopenharmony_ci	 * Do a second iteration over qpd_queues_list to check if any SDMA
2068c2ecf20Sopenharmony_ci	 * nodes got deleted while fetching SDMA counter.
2078c2ecf20Sopenharmony_ci	 */
2088c2ecf20Sopenharmony_ci	dqm_lock(dqm);
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	workarea->sdma_activity_counter += pdd->sdma_past_activity_counter;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	list_for_each_entry(q, &qpd->queues_list, list) {
2138c2ecf20Sopenharmony_ci		if (list_empty(&sdma_q_list.list))
2148c2ecf20Sopenharmony_ci			break;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci		if ((q->properties.type != KFD_QUEUE_TYPE_SDMA) &&
2178c2ecf20Sopenharmony_ci		    (q->properties.type != KFD_QUEUE_TYPE_SDMA_XGMI))
2188c2ecf20Sopenharmony_ci			continue;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci		list_for_each_entry_safe(sdma_q, next, &sdma_q_list.list, list) {
2218c2ecf20Sopenharmony_ci			if (((uint64_t __user *)q->properties.read_ptr == sdma_q->rptr) &&
2228c2ecf20Sopenharmony_ci			     (sdma_q->queue_id == q->properties.queue_id)) {
2238c2ecf20Sopenharmony_ci				list_del(&sdma_q->list);
2248c2ecf20Sopenharmony_ci				kfree(sdma_q);
2258c2ecf20Sopenharmony_ci				break;
2268c2ecf20Sopenharmony_ci			}
2278c2ecf20Sopenharmony_ci		}
2288c2ecf20Sopenharmony_ci	}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	dqm_unlock(dqm);
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	/*
2338c2ecf20Sopenharmony_ci	 * If temp list is not empty, it implies some queues got deleted
2348c2ecf20Sopenharmony_ci	 * from qpd->queues_list during SDMA usage read. Subtract the SDMA
2358c2ecf20Sopenharmony_ci	 * count for each node from the total SDMA count.
2368c2ecf20Sopenharmony_ci	 */
2378c2ecf20Sopenharmony_ci	list_for_each_entry_safe(sdma_q, next, &sdma_q_list.list, list) {
2388c2ecf20Sopenharmony_ci		workarea->sdma_activity_counter -= sdma_q->sdma_val;
2398c2ecf20Sopenharmony_ci		list_del(&sdma_q->list);
2408c2ecf20Sopenharmony_ci		kfree(sdma_q);
2418c2ecf20Sopenharmony_ci	}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	return;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_cicleanup:
2468c2ecf20Sopenharmony_ci	list_for_each_entry_safe(sdma_q, next, &sdma_q_list.list, list) {
2478c2ecf20Sopenharmony_ci		list_del(&sdma_q->list);
2488c2ecf20Sopenharmony_ci		kfree(sdma_q);
2498c2ecf20Sopenharmony_ci	}
2508c2ecf20Sopenharmony_ci}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci/**
2538c2ecf20Sopenharmony_ci * @kfd_get_cu_occupancy() - Collect number of waves in-flight on this device
2548c2ecf20Sopenharmony_ci * by current process. Translates acquired wave count into number of compute units
2558c2ecf20Sopenharmony_ci * that are occupied.
2568c2ecf20Sopenharmony_ci *
2578c2ecf20Sopenharmony_ci * @atr: Handle of attribute that allows reporting of wave count. The attribute
2588c2ecf20Sopenharmony_ci * handle encapsulates GPU device it is associated with, thereby allowing collection
2598c2ecf20Sopenharmony_ci * of waves in flight, etc
2608c2ecf20Sopenharmony_ci *
2618c2ecf20Sopenharmony_ci * @buffer: Handle of user provided buffer updated with wave count
2628c2ecf20Sopenharmony_ci *
2638c2ecf20Sopenharmony_ci * Return: Number of bytes written to user buffer or an error value
2648c2ecf20Sopenharmony_ci */
2658c2ecf20Sopenharmony_cistatic int kfd_get_cu_occupancy(struct attribute *attr, char *buffer)
2668c2ecf20Sopenharmony_ci{
2678c2ecf20Sopenharmony_ci	int cu_cnt;
2688c2ecf20Sopenharmony_ci	int wave_cnt;
2698c2ecf20Sopenharmony_ci	int max_waves_per_cu;
2708c2ecf20Sopenharmony_ci	struct kfd_dev *dev = NULL;
2718c2ecf20Sopenharmony_ci	struct kfd_process *proc = NULL;
2728c2ecf20Sopenharmony_ci	struct kfd_process_device *pdd = NULL;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	pdd = container_of(attr, struct kfd_process_device, attr_cu_occupancy);
2758c2ecf20Sopenharmony_ci	dev = pdd->dev;
2768c2ecf20Sopenharmony_ci	if (dev->kfd2kgd->get_cu_occupancy == NULL)
2778c2ecf20Sopenharmony_ci		return -EINVAL;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	cu_cnt = 0;
2808c2ecf20Sopenharmony_ci	proc = pdd->process;
2818c2ecf20Sopenharmony_ci	if (pdd->qpd.queue_count == 0) {
2828c2ecf20Sopenharmony_ci		pr_debug("Gpu-Id: %d has no active queues for process %d\n",
2838c2ecf20Sopenharmony_ci			 dev->id, proc->pasid);
2848c2ecf20Sopenharmony_ci		return snprintf(buffer, PAGE_SIZE, "%d\n", cu_cnt);
2858c2ecf20Sopenharmony_ci	}
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	/* Collect wave count from device if it supports */
2888c2ecf20Sopenharmony_ci	wave_cnt = 0;
2898c2ecf20Sopenharmony_ci	max_waves_per_cu = 0;
2908c2ecf20Sopenharmony_ci	dev->kfd2kgd->get_cu_occupancy(dev->kgd, proc->pasid, &wave_cnt,
2918c2ecf20Sopenharmony_ci			&max_waves_per_cu);
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	/* Translate wave count to number of compute units */
2948c2ecf20Sopenharmony_ci	cu_cnt = (wave_cnt + (max_waves_per_cu - 1)) / max_waves_per_cu;
2958c2ecf20Sopenharmony_ci	return snprintf(buffer, PAGE_SIZE, "%d\n", cu_cnt);
2968c2ecf20Sopenharmony_ci}
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_cistatic ssize_t kfd_procfs_show(struct kobject *kobj, struct attribute *attr,
2998c2ecf20Sopenharmony_ci			       char *buffer)
3008c2ecf20Sopenharmony_ci{
3018c2ecf20Sopenharmony_ci	if (strcmp(attr->name, "pasid") == 0) {
3028c2ecf20Sopenharmony_ci		struct kfd_process *p = container_of(attr, struct kfd_process,
3038c2ecf20Sopenharmony_ci						     attr_pasid);
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci		return snprintf(buffer, PAGE_SIZE, "%d\n", p->pasid);
3068c2ecf20Sopenharmony_ci	} else if (strncmp(attr->name, "vram_", 5) == 0) {
3078c2ecf20Sopenharmony_ci		struct kfd_process_device *pdd = container_of(attr, struct kfd_process_device,
3088c2ecf20Sopenharmony_ci							      attr_vram);
3098c2ecf20Sopenharmony_ci		return snprintf(buffer, PAGE_SIZE, "%llu\n", READ_ONCE(pdd->vram_usage));
3108c2ecf20Sopenharmony_ci	} else if (strncmp(attr->name, "sdma_", 5) == 0) {
3118c2ecf20Sopenharmony_ci		struct kfd_process_device *pdd = container_of(attr, struct kfd_process_device,
3128c2ecf20Sopenharmony_ci							      attr_sdma);
3138c2ecf20Sopenharmony_ci		struct kfd_sdma_activity_handler_workarea sdma_activity_work_handler;
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci		INIT_WORK(&sdma_activity_work_handler.sdma_activity_work,
3168c2ecf20Sopenharmony_ci					kfd_sdma_activity_worker);
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci		sdma_activity_work_handler.pdd = pdd;
3198c2ecf20Sopenharmony_ci		sdma_activity_work_handler.sdma_activity_counter = 0;
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci		schedule_work(&sdma_activity_work_handler.sdma_activity_work);
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci		flush_work(&sdma_activity_work_handler.sdma_activity_work);
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci		return snprintf(buffer, PAGE_SIZE, "%llu\n",
3268c2ecf20Sopenharmony_ci				(sdma_activity_work_handler.sdma_activity_counter)/
3278c2ecf20Sopenharmony_ci				 SDMA_ACTIVITY_DIVISOR);
3288c2ecf20Sopenharmony_ci	} else {
3298c2ecf20Sopenharmony_ci		pr_err("Invalid attribute");
3308c2ecf20Sopenharmony_ci		return -EINVAL;
3318c2ecf20Sopenharmony_ci	}
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	return 0;
3348c2ecf20Sopenharmony_ci}
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_cistatic void kfd_procfs_kobj_release(struct kobject *kobj)
3378c2ecf20Sopenharmony_ci{
3388c2ecf20Sopenharmony_ci	kfree(kobj);
3398c2ecf20Sopenharmony_ci}
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_cistatic const struct sysfs_ops kfd_procfs_ops = {
3428c2ecf20Sopenharmony_ci	.show = kfd_procfs_show,
3438c2ecf20Sopenharmony_ci};
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_cistatic struct kobj_type procfs_type = {
3468c2ecf20Sopenharmony_ci	.release = kfd_procfs_kobj_release,
3478c2ecf20Sopenharmony_ci	.sysfs_ops = &kfd_procfs_ops,
3488c2ecf20Sopenharmony_ci};
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_civoid kfd_procfs_init(void)
3518c2ecf20Sopenharmony_ci{
3528c2ecf20Sopenharmony_ci	int ret = 0;
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	procfs.kobj = kfd_alloc_struct(procfs.kobj);
3558c2ecf20Sopenharmony_ci	if (!procfs.kobj)
3568c2ecf20Sopenharmony_ci		return;
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	ret = kobject_init_and_add(procfs.kobj, &procfs_type,
3598c2ecf20Sopenharmony_ci				   &kfd_device->kobj, "proc");
3608c2ecf20Sopenharmony_ci	if (ret) {
3618c2ecf20Sopenharmony_ci		pr_warn("Could not create procfs proc folder");
3628c2ecf20Sopenharmony_ci		/* If we fail to create the procfs, clean up */
3638c2ecf20Sopenharmony_ci		kfd_procfs_shutdown();
3648c2ecf20Sopenharmony_ci	}
3658c2ecf20Sopenharmony_ci}
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_civoid kfd_procfs_shutdown(void)
3688c2ecf20Sopenharmony_ci{
3698c2ecf20Sopenharmony_ci	if (procfs.kobj) {
3708c2ecf20Sopenharmony_ci		kobject_del(procfs.kobj);
3718c2ecf20Sopenharmony_ci		kobject_put(procfs.kobj);
3728c2ecf20Sopenharmony_ci		procfs.kobj = NULL;
3738c2ecf20Sopenharmony_ci	}
3748c2ecf20Sopenharmony_ci}
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_cistatic ssize_t kfd_procfs_queue_show(struct kobject *kobj,
3778c2ecf20Sopenharmony_ci				     struct attribute *attr, char *buffer)
3788c2ecf20Sopenharmony_ci{
3798c2ecf20Sopenharmony_ci	struct queue *q = container_of(kobj, struct queue, kobj);
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	if (!strcmp(attr->name, "size"))
3828c2ecf20Sopenharmony_ci		return snprintf(buffer, PAGE_SIZE, "%llu",
3838c2ecf20Sopenharmony_ci				q->properties.queue_size);
3848c2ecf20Sopenharmony_ci	else if (!strcmp(attr->name, "type"))
3858c2ecf20Sopenharmony_ci		return snprintf(buffer, PAGE_SIZE, "%d", q->properties.type);
3868c2ecf20Sopenharmony_ci	else if (!strcmp(attr->name, "gpuid"))
3878c2ecf20Sopenharmony_ci		return snprintf(buffer, PAGE_SIZE, "%u", q->device->id);
3888c2ecf20Sopenharmony_ci	else
3898c2ecf20Sopenharmony_ci		pr_err("Invalid attribute");
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	return 0;
3928c2ecf20Sopenharmony_ci}
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_cistatic ssize_t kfd_procfs_stats_show(struct kobject *kobj,
3958c2ecf20Sopenharmony_ci				     struct attribute *attr, char *buffer)
3968c2ecf20Sopenharmony_ci{
3978c2ecf20Sopenharmony_ci	if (strcmp(attr->name, "evicted_ms") == 0) {
3988c2ecf20Sopenharmony_ci		struct kfd_process_device *pdd = container_of(attr,
3998c2ecf20Sopenharmony_ci				struct kfd_process_device,
4008c2ecf20Sopenharmony_ci				attr_evict);
4018c2ecf20Sopenharmony_ci		uint64_t evict_jiffies;
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci		evict_jiffies = atomic64_read(&pdd->evict_duration_counter);
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci		return snprintf(buffer,
4068c2ecf20Sopenharmony_ci				PAGE_SIZE,
4078c2ecf20Sopenharmony_ci				"%llu\n",
4088c2ecf20Sopenharmony_ci				jiffies64_to_msecs(evict_jiffies));
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	/* Sysfs handle that gets CU occupancy is per device */
4118c2ecf20Sopenharmony_ci	} else if (strcmp(attr->name, "cu_occupancy") == 0) {
4128c2ecf20Sopenharmony_ci		return kfd_get_cu_occupancy(attr, buffer);
4138c2ecf20Sopenharmony_ci	} else {
4148c2ecf20Sopenharmony_ci		pr_err("Invalid attribute");
4158c2ecf20Sopenharmony_ci	}
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	return 0;
4188c2ecf20Sopenharmony_ci}
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_cistatic struct attribute attr_queue_size = {
4218c2ecf20Sopenharmony_ci	.name = "size",
4228c2ecf20Sopenharmony_ci	.mode = KFD_SYSFS_FILE_MODE
4238c2ecf20Sopenharmony_ci};
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_cistatic struct attribute attr_queue_type = {
4268c2ecf20Sopenharmony_ci	.name = "type",
4278c2ecf20Sopenharmony_ci	.mode = KFD_SYSFS_FILE_MODE
4288c2ecf20Sopenharmony_ci};
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_cistatic struct attribute attr_queue_gpuid = {
4318c2ecf20Sopenharmony_ci	.name = "gpuid",
4328c2ecf20Sopenharmony_ci	.mode = KFD_SYSFS_FILE_MODE
4338c2ecf20Sopenharmony_ci};
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_cistatic struct attribute *procfs_queue_attrs[] = {
4368c2ecf20Sopenharmony_ci	&attr_queue_size,
4378c2ecf20Sopenharmony_ci	&attr_queue_type,
4388c2ecf20Sopenharmony_ci	&attr_queue_gpuid,
4398c2ecf20Sopenharmony_ci	NULL
4408c2ecf20Sopenharmony_ci};
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_cistatic const struct sysfs_ops procfs_queue_ops = {
4438c2ecf20Sopenharmony_ci	.show = kfd_procfs_queue_show,
4448c2ecf20Sopenharmony_ci};
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_cistatic struct kobj_type procfs_queue_type = {
4478c2ecf20Sopenharmony_ci	.sysfs_ops = &procfs_queue_ops,
4488c2ecf20Sopenharmony_ci	.default_attrs = procfs_queue_attrs,
4498c2ecf20Sopenharmony_ci};
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_cistatic const struct sysfs_ops procfs_stats_ops = {
4528c2ecf20Sopenharmony_ci	.show = kfd_procfs_stats_show,
4538c2ecf20Sopenharmony_ci};
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_cistatic struct kobj_type procfs_stats_type = {
4568c2ecf20Sopenharmony_ci	.sysfs_ops = &procfs_stats_ops,
4578c2ecf20Sopenharmony_ci	.release = kfd_procfs_kobj_release,
4588c2ecf20Sopenharmony_ci};
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ciint kfd_procfs_add_queue(struct queue *q)
4618c2ecf20Sopenharmony_ci{
4628c2ecf20Sopenharmony_ci	struct kfd_process *proc;
4638c2ecf20Sopenharmony_ci	int ret;
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	if (!q || !q->process)
4668c2ecf20Sopenharmony_ci		return -EINVAL;
4678c2ecf20Sopenharmony_ci	proc = q->process;
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	/* Create proc/<pid>/queues/<queue id> folder */
4708c2ecf20Sopenharmony_ci	if (!proc->kobj_queues)
4718c2ecf20Sopenharmony_ci		return -EFAULT;
4728c2ecf20Sopenharmony_ci	ret = kobject_init_and_add(&q->kobj, &procfs_queue_type,
4738c2ecf20Sopenharmony_ci			proc->kobj_queues, "%u", q->properties.queue_id);
4748c2ecf20Sopenharmony_ci	if (ret < 0) {
4758c2ecf20Sopenharmony_ci		pr_warn("Creating proc/<pid>/queues/%u failed",
4768c2ecf20Sopenharmony_ci			q->properties.queue_id);
4778c2ecf20Sopenharmony_ci		kobject_put(&q->kobj);
4788c2ecf20Sopenharmony_ci		return ret;
4798c2ecf20Sopenharmony_ci	}
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	return 0;
4828c2ecf20Sopenharmony_ci}
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_cistatic int kfd_sysfs_create_file(struct kfd_process *p, struct attribute *attr,
4858c2ecf20Sopenharmony_ci				 char *name)
4868c2ecf20Sopenharmony_ci{
4878c2ecf20Sopenharmony_ci	int ret = 0;
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	if (!p || !attr || !name)
4908c2ecf20Sopenharmony_ci		return -EINVAL;
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	attr->name = name;
4938c2ecf20Sopenharmony_ci	attr->mode = KFD_SYSFS_FILE_MODE;
4948c2ecf20Sopenharmony_ci	sysfs_attr_init(attr);
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	ret = sysfs_create_file(p->kobj, attr);
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci	return ret;
4998c2ecf20Sopenharmony_ci}
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_cistatic int kfd_procfs_add_sysfs_stats(struct kfd_process *p)
5028c2ecf20Sopenharmony_ci{
5038c2ecf20Sopenharmony_ci	int ret = 0;
5048c2ecf20Sopenharmony_ci	struct kfd_process_device *pdd;
5058c2ecf20Sopenharmony_ci	char stats_dir_filename[MAX_SYSFS_FILENAME_LEN];
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	if (!p)
5088c2ecf20Sopenharmony_ci		return -EINVAL;
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	if (!p->kobj)
5118c2ecf20Sopenharmony_ci		return -EFAULT;
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	/*
5148c2ecf20Sopenharmony_ci	 * Create sysfs files for each GPU:
5158c2ecf20Sopenharmony_ci	 * - proc/<pid>/stats_<gpuid>/
5168c2ecf20Sopenharmony_ci	 * - proc/<pid>/stats_<gpuid>/evicted_ms
5178c2ecf20Sopenharmony_ci	 * - proc/<pid>/stats_<gpuid>/cu_occupancy
5188c2ecf20Sopenharmony_ci	 */
5198c2ecf20Sopenharmony_ci	list_for_each_entry(pdd, &p->per_device_data, per_device_list) {
5208c2ecf20Sopenharmony_ci		struct kobject *kobj_stats;
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci		snprintf(stats_dir_filename, MAX_SYSFS_FILENAME_LEN,
5238c2ecf20Sopenharmony_ci				"stats_%u", pdd->dev->id);
5248c2ecf20Sopenharmony_ci		kobj_stats = kfd_alloc_struct(kobj_stats);
5258c2ecf20Sopenharmony_ci		if (!kobj_stats)
5268c2ecf20Sopenharmony_ci			return -ENOMEM;
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci		ret = kobject_init_and_add(kobj_stats,
5298c2ecf20Sopenharmony_ci						&procfs_stats_type,
5308c2ecf20Sopenharmony_ci						p->kobj,
5318c2ecf20Sopenharmony_ci						stats_dir_filename);
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci		if (ret) {
5348c2ecf20Sopenharmony_ci			pr_warn("Creating KFD proc/stats_%s folder failed",
5358c2ecf20Sopenharmony_ci					stats_dir_filename);
5368c2ecf20Sopenharmony_ci			kobject_put(kobj_stats);
5378c2ecf20Sopenharmony_ci			goto err;
5388c2ecf20Sopenharmony_ci		}
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci		pdd->kobj_stats = kobj_stats;
5418c2ecf20Sopenharmony_ci		pdd->attr_evict.name = "evicted_ms";
5428c2ecf20Sopenharmony_ci		pdd->attr_evict.mode = KFD_SYSFS_FILE_MODE;
5438c2ecf20Sopenharmony_ci		sysfs_attr_init(&pdd->attr_evict);
5448c2ecf20Sopenharmony_ci		ret = sysfs_create_file(kobj_stats, &pdd->attr_evict);
5458c2ecf20Sopenharmony_ci		if (ret)
5468c2ecf20Sopenharmony_ci			pr_warn("Creating eviction stats for gpuid %d failed",
5478c2ecf20Sopenharmony_ci					(int)pdd->dev->id);
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci		/* Add sysfs file to report compute unit occupancy */
5508c2ecf20Sopenharmony_ci		if (pdd->dev->kfd2kgd->get_cu_occupancy != NULL) {
5518c2ecf20Sopenharmony_ci			pdd->attr_cu_occupancy.name = "cu_occupancy";
5528c2ecf20Sopenharmony_ci			pdd->attr_cu_occupancy.mode = KFD_SYSFS_FILE_MODE;
5538c2ecf20Sopenharmony_ci			sysfs_attr_init(&pdd->attr_cu_occupancy);
5548c2ecf20Sopenharmony_ci			ret = sysfs_create_file(kobj_stats,
5558c2ecf20Sopenharmony_ci						&pdd->attr_cu_occupancy);
5568c2ecf20Sopenharmony_ci			if (ret)
5578c2ecf20Sopenharmony_ci				pr_warn("Creating %s failed for gpuid: %d",
5588c2ecf20Sopenharmony_ci					pdd->attr_cu_occupancy.name,
5598c2ecf20Sopenharmony_ci					(int)pdd->dev->id);
5608c2ecf20Sopenharmony_ci		}
5618c2ecf20Sopenharmony_ci	}
5628c2ecf20Sopenharmony_cierr:
5638c2ecf20Sopenharmony_ci	return ret;
5648c2ecf20Sopenharmony_ci}
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_cistatic int kfd_procfs_add_sysfs_files(struct kfd_process *p)
5688c2ecf20Sopenharmony_ci{
5698c2ecf20Sopenharmony_ci	int ret = 0;
5708c2ecf20Sopenharmony_ci	struct kfd_process_device *pdd;
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci	if (!p)
5738c2ecf20Sopenharmony_ci		return -EINVAL;
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	if (!p->kobj)
5768c2ecf20Sopenharmony_ci		return -EFAULT;
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci	/*
5798c2ecf20Sopenharmony_ci	 * Create sysfs files for each GPU:
5808c2ecf20Sopenharmony_ci	 * - proc/<pid>/vram_<gpuid>
5818c2ecf20Sopenharmony_ci	 * - proc/<pid>/sdma_<gpuid>
5828c2ecf20Sopenharmony_ci	 */
5838c2ecf20Sopenharmony_ci	list_for_each_entry(pdd, &p->per_device_data, per_device_list) {
5848c2ecf20Sopenharmony_ci		snprintf(pdd->vram_filename, MAX_SYSFS_FILENAME_LEN, "vram_%u",
5858c2ecf20Sopenharmony_ci			 pdd->dev->id);
5868c2ecf20Sopenharmony_ci		ret = kfd_sysfs_create_file(p, &pdd->attr_vram, pdd->vram_filename);
5878c2ecf20Sopenharmony_ci		if (ret)
5888c2ecf20Sopenharmony_ci			pr_warn("Creating vram usage for gpu id %d failed",
5898c2ecf20Sopenharmony_ci				(int)pdd->dev->id);
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci		snprintf(pdd->sdma_filename, MAX_SYSFS_FILENAME_LEN, "sdma_%u",
5928c2ecf20Sopenharmony_ci			 pdd->dev->id);
5938c2ecf20Sopenharmony_ci		ret = kfd_sysfs_create_file(p, &pdd->attr_sdma, pdd->sdma_filename);
5948c2ecf20Sopenharmony_ci		if (ret)
5958c2ecf20Sopenharmony_ci			pr_warn("Creating sdma usage for gpu id %d failed",
5968c2ecf20Sopenharmony_ci				(int)pdd->dev->id);
5978c2ecf20Sopenharmony_ci	}
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci	return ret;
6008c2ecf20Sopenharmony_ci}
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_civoid kfd_procfs_del_queue(struct queue *q)
6038c2ecf20Sopenharmony_ci{
6048c2ecf20Sopenharmony_ci	if (!q)
6058c2ecf20Sopenharmony_ci		return;
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci	kobject_del(&q->kobj);
6088c2ecf20Sopenharmony_ci	kobject_put(&q->kobj);
6098c2ecf20Sopenharmony_ci}
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ciint kfd_process_create_wq(void)
6128c2ecf20Sopenharmony_ci{
6138c2ecf20Sopenharmony_ci	if (!kfd_process_wq)
6148c2ecf20Sopenharmony_ci		kfd_process_wq = alloc_workqueue("kfd_process_wq", 0, 0);
6158c2ecf20Sopenharmony_ci	if (!kfd_restore_wq)
6168c2ecf20Sopenharmony_ci		kfd_restore_wq = alloc_ordered_workqueue("kfd_restore_wq", 0);
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci	if (!kfd_process_wq || !kfd_restore_wq) {
6198c2ecf20Sopenharmony_ci		kfd_process_destroy_wq();
6208c2ecf20Sopenharmony_ci		return -ENOMEM;
6218c2ecf20Sopenharmony_ci	}
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci	return 0;
6248c2ecf20Sopenharmony_ci}
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_civoid kfd_process_destroy_wq(void)
6278c2ecf20Sopenharmony_ci{
6288c2ecf20Sopenharmony_ci	if (kfd_process_wq) {
6298c2ecf20Sopenharmony_ci		destroy_workqueue(kfd_process_wq);
6308c2ecf20Sopenharmony_ci		kfd_process_wq = NULL;
6318c2ecf20Sopenharmony_ci	}
6328c2ecf20Sopenharmony_ci	if (kfd_restore_wq) {
6338c2ecf20Sopenharmony_ci		destroy_workqueue(kfd_restore_wq);
6348c2ecf20Sopenharmony_ci		kfd_restore_wq = NULL;
6358c2ecf20Sopenharmony_ci	}
6368c2ecf20Sopenharmony_ci}
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_cistatic void kfd_process_free_gpuvm(struct kgd_mem *mem,
6398c2ecf20Sopenharmony_ci			struct kfd_process_device *pdd)
6408c2ecf20Sopenharmony_ci{
6418c2ecf20Sopenharmony_ci	struct kfd_dev *dev = pdd->dev;
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci	amdgpu_amdkfd_gpuvm_unmap_memory_from_gpu(dev->kgd, mem, pdd->vm);
6448c2ecf20Sopenharmony_ci	amdgpu_amdkfd_gpuvm_free_memory_of_gpu(dev->kgd, mem, NULL);
6458c2ecf20Sopenharmony_ci}
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci/* kfd_process_alloc_gpuvm - Allocate GPU VM for the KFD process
6488c2ecf20Sopenharmony_ci *	This function should be only called right after the process
6498c2ecf20Sopenharmony_ci *	is created and when kfd_processes_mutex is still being held
6508c2ecf20Sopenharmony_ci *	to avoid concurrency. Because of that exclusiveness, we do
6518c2ecf20Sopenharmony_ci *	not need to take p->mutex.
6528c2ecf20Sopenharmony_ci */
6538c2ecf20Sopenharmony_cistatic int kfd_process_alloc_gpuvm(struct kfd_process_device *pdd,
6548c2ecf20Sopenharmony_ci				   uint64_t gpu_va, uint32_t size,
6558c2ecf20Sopenharmony_ci				   uint32_t flags, void **kptr)
6568c2ecf20Sopenharmony_ci{
6578c2ecf20Sopenharmony_ci	struct kfd_dev *kdev = pdd->dev;
6588c2ecf20Sopenharmony_ci	struct kgd_mem *mem = NULL;
6598c2ecf20Sopenharmony_ci	int handle;
6608c2ecf20Sopenharmony_ci	int err;
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci	err = amdgpu_amdkfd_gpuvm_alloc_memory_of_gpu(kdev->kgd, gpu_va, size,
6638c2ecf20Sopenharmony_ci						 pdd->vm, &mem, NULL, flags);
6648c2ecf20Sopenharmony_ci	if (err)
6658c2ecf20Sopenharmony_ci		goto err_alloc_mem;
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci	err = amdgpu_amdkfd_gpuvm_map_memory_to_gpu(kdev->kgd, mem, pdd->vm);
6688c2ecf20Sopenharmony_ci	if (err)
6698c2ecf20Sopenharmony_ci		goto err_map_mem;
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci	err = amdgpu_amdkfd_gpuvm_sync_memory(kdev->kgd, mem, true);
6728c2ecf20Sopenharmony_ci	if (err) {
6738c2ecf20Sopenharmony_ci		pr_debug("Sync memory failed, wait interrupted by user signal\n");
6748c2ecf20Sopenharmony_ci		goto sync_memory_failed;
6758c2ecf20Sopenharmony_ci	}
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci	/* Create an obj handle so kfd_process_device_remove_obj_handle
6788c2ecf20Sopenharmony_ci	 * will take care of the bo removal when the process finishes.
6798c2ecf20Sopenharmony_ci	 * We do not need to take p->mutex, because the process is just
6808c2ecf20Sopenharmony_ci	 * created and the ioctls have not had the chance to run.
6818c2ecf20Sopenharmony_ci	 */
6828c2ecf20Sopenharmony_ci	handle = kfd_process_device_create_obj_handle(pdd, mem);
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_ci	if (handle < 0) {
6858c2ecf20Sopenharmony_ci		err = handle;
6868c2ecf20Sopenharmony_ci		goto free_gpuvm;
6878c2ecf20Sopenharmony_ci	}
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci	if (kptr) {
6908c2ecf20Sopenharmony_ci		err = amdgpu_amdkfd_gpuvm_map_gtt_bo_to_kernel(kdev->kgd,
6918c2ecf20Sopenharmony_ci				(struct kgd_mem *)mem, kptr, NULL);
6928c2ecf20Sopenharmony_ci		if (err) {
6938c2ecf20Sopenharmony_ci			pr_debug("Map GTT BO to kernel failed\n");
6948c2ecf20Sopenharmony_ci			goto free_obj_handle;
6958c2ecf20Sopenharmony_ci		}
6968c2ecf20Sopenharmony_ci	}
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	return err;
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_cifree_obj_handle:
7018c2ecf20Sopenharmony_ci	kfd_process_device_remove_obj_handle(pdd, handle);
7028c2ecf20Sopenharmony_cifree_gpuvm:
7038c2ecf20Sopenharmony_cisync_memory_failed:
7048c2ecf20Sopenharmony_ci	kfd_process_free_gpuvm(mem, pdd);
7058c2ecf20Sopenharmony_ci	return err;
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_cierr_map_mem:
7088c2ecf20Sopenharmony_ci	amdgpu_amdkfd_gpuvm_free_memory_of_gpu(kdev->kgd, mem, NULL);
7098c2ecf20Sopenharmony_cierr_alloc_mem:
7108c2ecf20Sopenharmony_ci	*kptr = NULL;
7118c2ecf20Sopenharmony_ci	return err;
7128c2ecf20Sopenharmony_ci}
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci/* kfd_process_device_reserve_ib_mem - Reserve memory inside the
7158c2ecf20Sopenharmony_ci *	process for IB usage The memory reserved is for KFD to submit
7168c2ecf20Sopenharmony_ci *	IB to AMDGPU from kernel.  If the memory is reserved
7178c2ecf20Sopenharmony_ci *	successfully, ib_kaddr will have the CPU/kernel
7188c2ecf20Sopenharmony_ci *	address. Check ib_kaddr before accessing the memory.
7198c2ecf20Sopenharmony_ci */
7208c2ecf20Sopenharmony_cistatic int kfd_process_device_reserve_ib_mem(struct kfd_process_device *pdd)
7218c2ecf20Sopenharmony_ci{
7228c2ecf20Sopenharmony_ci	struct qcm_process_device *qpd = &pdd->qpd;
7238c2ecf20Sopenharmony_ci	uint32_t flags = KFD_IOC_ALLOC_MEM_FLAGS_GTT |
7248c2ecf20Sopenharmony_ci			KFD_IOC_ALLOC_MEM_FLAGS_NO_SUBSTITUTE |
7258c2ecf20Sopenharmony_ci			KFD_IOC_ALLOC_MEM_FLAGS_WRITABLE |
7268c2ecf20Sopenharmony_ci			KFD_IOC_ALLOC_MEM_FLAGS_EXECUTABLE;
7278c2ecf20Sopenharmony_ci	void *kaddr;
7288c2ecf20Sopenharmony_ci	int ret;
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ci	if (qpd->ib_kaddr || !qpd->ib_base)
7318c2ecf20Sopenharmony_ci		return 0;
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	/* ib_base is only set for dGPU */
7348c2ecf20Sopenharmony_ci	ret = kfd_process_alloc_gpuvm(pdd, qpd->ib_base, PAGE_SIZE, flags,
7358c2ecf20Sopenharmony_ci				      &kaddr);
7368c2ecf20Sopenharmony_ci	if (ret)
7378c2ecf20Sopenharmony_ci		return ret;
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_ci	qpd->ib_kaddr = kaddr;
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci	return 0;
7428c2ecf20Sopenharmony_ci}
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_cistruct kfd_process *kfd_create_process(struct file *filep)
7458c2ecf20Sopenharmony_ci{
7468c2ecf20Sopenharmony_ci	struct kfd_process *process;
7478c2ecf20Sopenharmony_ci	struct task_struct *thread = current;
7488c2ecf20Sopenharmony_ci	int ret;
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci	if (!thread->mm)
7518c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ci	/* Only the pthreads threading model is supported. */
7548c2ecf20Sopenharmony_ci	if (thread->group_leader->mm != thread->mm)
7558c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci	/*
7588c2ecf20Sopenharmony_ci	 * take kfd processes mutex before starting of process creation
7598c2ecf20Sopenharmony_ci	 * so there won't be a case where two threads of the same process
7608c2ecf20Sopenharmony_ci	 * create two kfd_process structures
7618c2ecf20Sopenharmony_ci	 */
7628c2ecf20Sopenharmony_ci	mutex_lock(&kfd_processes_mutex);
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci	/* A prior open of /dev/kfd could have already created the process. */
7658c2ecf20Sopenharmony_ci	process = find_process(thread);
7668c2ecf20Sopenharmony_ci	if (process) {
7678c2ecf20Sopenharmony_ci		pr_debug("Process already found\n");
7688c2ecf20Sopenharmony_ci	} else {
7698c2ecf20Sopenharmony_ci		process = create_process(thread);
7708c2ecf20Sopenharmony_ci		if (IS_ERR(process))
7718c2ecf20Sopenharmony_ci			goto out;
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_ci		ret = kfd_process_init_cwsr_apu(process, filep);
7748c2ecf20Sopenharmony_ci		if (ret) {
7758c2ecf20Sopenharmony_ci			process = ERR_PTR(ret);
7768c2ecf20Sopenharmony_ci			goto out;
7778c2ecf20Sopenharmony_ci		}
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci		if (!procfs.kobj)
7808c2ecf20Sopenharmony_ci			goto out;
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci		process->kobj = kfd_alloc_struct(process->kobj);
7838c2ecf20Sopenharmony_ci		if (!process->kobj) {
7848c2ecf20Sopenharmony_ci			pr_warn("Creating procfs kobject failed");
7858c2ecf20Sopenharmony_ci			goto out;
7868c2ecf20Sopenharmony_ci		}
7878c2ecf20Sopenharmony_ci		ret = kobject_init_and_add(process->kobj, &procfs_type,
7888c2ecf20Sopenharmony_ci					   procfs.kobj, "%d",
7898c2ecf20Sopenharmony_ci					   (int)process->lead_thread->pid);
7908c2ecf20Sopenharmony_ci		if (ret) {
7918c2ecf20Sopenharmony_ci			pr_warn("Creating procfs pid directory failed");
7928c2ecf20Sopenharmony_ci			kobject_put(process->kobj);
7938c2ecf20Sopenharmony_ci			goto out;
7948c2ecf20Sopenharmony_ci		}
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ci		process->attr_pasid.name = "pasid";
7978c2ecf20Sopenharmony_ci		process->attr_pasid.mode = KFD_SYSFS_FILE_MODE;
7988c2ecf20Sopenharmony_ci		sysfs_attr_init(&process->attr_pasid);
7998c2ecf20Sopenharmony_ci		ret = sysfs_create_file(process->kobj, &process->attr_pasid);
8008c2ecf20Sopenharmony_ci		if (ret)
8018c2ecf20Sopenharmony_ci			pr_warn("Creating pasid for pid %d failed",
8028c2ecf20Sopenharmony_ci					(int)process->lead_thread->pid);
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_ci		process->kobj_queues = kobject_create_and_add("queues",
8058c2ecf20Sopenharmony_ci							process->kobj);
8068c2ecf20Sopenharmony_ci		if (!process->kobj_queues)
8078c2ecf20Sopenharmony_ci			pr_warn("Creating KFD proc/queues folder failed");
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci		ret = kfd_procfs_add_sysfs_stats(process);
8108c2ecf20Sopenharmony_ci		if (ret)
8118c2ecf20Sopenharmony_ci			pr_warn("Creating sysfs stats dir for pid %d failed",
8128c2ecf20Sopenharmony_ci				(int)process->lead_thread->pid);
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_ci		ret = kfd_procfs_add_sysfs_files(process);
8158c2ecf20Sopenharmony_ci		if (ret)
8168c2ecf20Sopenharmony_ci			pr_warn("Creating sysfs usage file for pid %d failed",
8178c2ecf20Sopenharmony_ci				(int)process->lead_thread->pid);
8188c2ecf20Sopenharmony_ci	}
8198c2ecf20Sopenharmony_ciout:
8208c2ecf20Sopenharmony_ci	if (!IS_ERR(process))
8218c2ecf20Sopenharmony_ci		kref_get(&process->ref);
8228c2ecf20Sopenharmony_ci	mutex_unlock(&kfd_processes_mutex);
8238c2ecf20Sopenharmony_ci
8248c2ecf20Sopenharmony_ci	return process;
8258c2ecf20Sopenharmony_ci}
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_cistruct kfd_process *kfd_get_process(const struct task_struct *thread)
8288c2ecf20Sopenharmony_ci{
8298c2ecf20Sopenharmony_ci	struct kfd_process *process;
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_ci	if (!thread->mm)
8328c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
8338c2ecf20Sopenharmony_ci
8348c2ecf20Sopenharmony_ci	/* Only the pthreads threading model is supported. */
8358c2ecf20Sopenharmony_ci	if (thread->group_leader->mm != thread->mm)
8368c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci	process = find_process(thread);
8398c2ecf20Sopenharmony_ci	if (!process)
8408c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_ci	return process;
8438c2ecf20Sopenharmony_ci}
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_cistatic struct kfd_process *find_process_by_mm(const struct mm_struct *mm)
8468c2ecf20Sopenharmony_ci{
8478c2ecf20Sopenharmony_ci	struct kfd_process *process;
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_ci	hash_for_each_possible_rcu(kfd_processes_table, process,
8508c2ecf20Sopenharmony_ci					kfd_processes, (uintptr_t)mm)
8518c2ecf20Sopenharmony_ci		if (process->mm == mm)
8528c2ecf20Sopenharmony_ci			return process;
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_ci	return NULL;
8558c2ecf20Sopenharmony_ci}
8568c2ecf20Sopenharmony_ci
8578c2ecf20Sopenharmony_cistatic struct kfd_process *find_process(const struct task_struct *thread)
8588c2ecf20Sopenharmony_ci{
8598c2ecf20Sopenharmony_ci	struct kfd_process *p;
8608c2ecf20Sopenharmony_ci	int idx;
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_ci	idx = srcu_read_lock(&kfd_processes_srcu);
8638c2ecf20Sopenharmony_ci	p = find_process_by_mm(thread->mm);
8648c2ecf20Sopenharmony_ci	srcu_read_unlock(&kfd_processes_srcu, idx);
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci	return p;
8678c2ecf20Sopenharmony_ci}
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_civoid kfd_unref_process(struct kfd_process *p)
8708c2ecf20Sopenharmony_ci{
8718c2ecf20Sopenharmony_ci	kref_put(&p->ref, kfd_process_ref_release);
8728c2ecf20Sopenharmony_ci}
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_cistatic void kfd_process_device_free_bos(struct kfd_process_device *pdd)
8758c2ecf20Sopenharmony_ci{
8768c2ecf20Sopenharmony_ci	struct kfd_process *p = pdd->process;
8778c2ecf20Sopenharmony_ci	void *mem;
8788c2ecf20Sopenharmony_ci	int id;
8798c2ecf20Sopenharmony_ci
8808c2ecf20Sopenharmony_ci	/*
8818c2ecf20Sopenharmony_ci	 * Remove all handles from idr and release appropriate
8828c2ecf20Sopenharmony_ci	 * local memory object
8838c2ecf20Sopenharmony_ci	 */
8848c2ecf20Sopenharmony_ci	idr_for_each_entry(&pdd->alloc_idr, mem, id) {
8858c2ecf20Sopenharmony_ci		struct kfd_process_device *peer_pdd;
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_ci		list_for_each_entry(peer_pdd, &p->per_device_data,
8888c2ecf20Sopenharmony_ci				    per_device_list) {
8898c2ecf20Sopenharmony_ci			if (!peer_pdd->vm)
8908c2ecf20Sopenharmony_ci				continue;
8918c2ecf20Sopenharmony_ci			amdgpu_amdkfd_gpuvm_unmap_memory_from_gpu(
8928c2ecf20Sopenharmony_ci				peer_pdd->dev->kgd, mem, peer_pdd->vm);
8938c2ecf20Sopenharmony_ci		}
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_ci		amdgpu_amdkfd_gpuvm_free_memory_of_gpu(pdd->dev->kgd, mem, NULL);
8968c2ecf20Sopenharmony_ci		kfd_process_device_remove_obj_handle(pdd, id);
8978c2ecf20Sopenharmony_ci	}
8988c2ecf20Sopenharmony_ci}
8998c2ecf20Sopenharmony_ci
9008c2ecf20Sopenharmony_cistatic void kfd_process_free_outstanding_kfd_bos(struct kfd_process *p)
9018c2ecf20Sopenharmony_ci{
9028c2ecf20Sopenharmony_ci	struct kfd_process_device *pdd;
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci	list_for_each_entry(pdd, &p->per_device_data, per_device_list)
9058c2ecf20Sopenharmony_ci		kfd_process_device_free_bos(pdd);
9068c2ecf20Sopenharmony_ci}
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_cistatic void kfd_process_destroy_pdds(struct kfd_process *p)
9098c2ecf20Sopenharmony_ci{
9108c2ecf20Sopenharmony_ci	struct kfd_process_device *pdd, *temp;
9118c2ecf20Sopenharmony_ci
9128c2ecf20Sopenharmony_ci	list_for_each_entry_safe(pdd, temp, &p->per_device_data,
9138c2ecf20Sopenharmony_ci				 per_device_list) {
9148c2ecf20Sopenharmony_ci		pr_debug("Releasing pdd (topology id %d) for process (pasid 0x%x)\n",
9158c2ecf20Sopenharmony_ci				pdd->dev->id, p->pasid);
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_ci		if (pdd->drm_file) {
9188c2ecf20Sopenharmony_ci			amdgpu_amdkfd_gpuvm_release_process_vm(
9198c2ecf20Sopenharmony_ci					pdd->dev->kgd, pdd->vm);
9208c2ecf20Sopenharmony_ci			fput(pdd->drm_file);
9218c2ecf20Sopenharmony_ci		}
9228c2ecf20Sopenharmony_ci		else if (pdd->vm)
9238c2ecf20Sopenharmony_ci			amdgpu_amdkfd_gpuvm_destroy_process_vm(
9248c2ecf20Sopenharmony_ci				pdd->dev->kgd, pdd->vm);
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_ci		list_del(&pdd->per_device_list);
9278c2ecf20Sopenharmony_ci
9288c2ecf20Sopenharmony_ci		if (pdd->qpd.cwsr_kaddr && !pdd->qpd.cwsr_base)
9298c2ecf20Sopenharmony_ci			free_pages((unsigned long)pdd->qpd.cwsr_kaddr,
9308c2ecf20Sopenharmony_ci				get_order(KFD_CWSR_TBA_TMA_SIZE));
9318c2ecf20Sopenharmony_ci
9328c2ecf20Sopenharmony_ci		kfree(pdd->qpd.doorbell_bitmap);
9338c2ecf20Sopenharmony_ci		idr_destroy(&pdd->alloc_idr);
9348c2ecf20Sopenharmony_ci
9358c2ecf20Sopenharmony_ci		kfd_free_process_doorbells(pdd->dev, pdd->doorbell_index);
9368c2ecf20Sopenharmony_ci
9378c2ecf20Sopenharmony_ci		/*
9388c2ecf20Sopenharmony_ci		 * before destroying pdd, make sure to report availability
9398c2ecf20Sopenharmony_ci		 * for auto suspend
9408c2ecf20Sopenharmony_ci		 */
9418c2ecf20Sopenharmony_ci		if (pdd->runtime_inuse) {
9428c2ecf20Sopenharmony_ci			pm_runtime_mark_last_busy(pdd->dev->ddev->dev);
9438c2ecf20Sopenharmony_ci			pm_runtime_put_autosuspend(pdd->dev->ddev->dev);
9448c2ecf20Sopenharmony_ci			pdd->runtime_inuse = false;
9458c2ecf20Sopenharmony_ci		}
9468c2ecf20Sopenharmony_ci
9478c2ecf20Sopenharmony_ci		kfree(pdd);
9488c2ecf20Sopenharmony_ci	}
9498c2ecf20Sopenharmony_ci}
9508c2ecf20Sopenharmony_ci
9518c2ecf20Sopenharmony_ci/* No process locking is needed in this function, because the process
9528c2ecf20Sopenharmony_ci * is not findable any more. We must assume that no other thread is
9538c2ecf20Sopenharmony_ci * using it any more, otherwise we couldn't safely free the process
9548c2ecf20Sopenharmony_ci * structure in the end.
9558c2ecf20Sopenharmony_ci */
9568c2ecf20Sopenharmony_cistatic void kfd_process_wq_release(struct work_struct *work)
9578c2ecf20Sopenharmony_ci{
9588c2ecf20Sopenharmony_ci	struct kfd_process *p = container_of(work, struct kfd_process,
9598c2ecf20Sopenharmony_ci					     release_work);
9608c2ecf20Sopenharmony_ci	struct kfd_process_device *pdd;
9618c2ecf20Sopenharmony_ci
9628c2ecf20Sopenharmony_ci	/* Remove the procfs files */
9638c2ecf20Sopenharmony_ci	if (p->kobj) {
9648c2ecf20Sopenharmony_ci		sysfs_remove_file(p->kobj, &p->attr_pasid);
9658c2ecf20Sopenharmony_ci		kobject_del(p->kobj_queues);
9668c2ecf20Sopenharmony_ci		kobject_put(p->kobj_queues);
9678c2ecf20Sopenharmony_ci		p->kobj_queues = NULL;
9688c2ecf20Sopenharmony_ci
9698c2ecf20Sopenharmony_ci		list_for_each_entry(pdd, &p->per_device_data, per_device_list) {
9708c2ecf20Sopenharmony_ci			sysfs_remove_file(p->kobj, &pdd->attr_vram);
9718c2ecf20Sopenharmony_ci			sysfs_remove_file(p->kobj, &pdd->attr_sdma);
9728c2ecf20Sopenharmony_ci
9738c2ecf20Sopenharmony_ci			sysfs_remove_file(pdd->kobj_stats, &pdd->attr_evict);
9748c2ecf20Sopenharmony_ci			if (pdd->dev->kfd2kgd->get_cu_occupancy)
9758c2ecf20Sopenharmony_ci				sysfs_remove_file(pdd->kobj_stats,
9768c2ecf20Sopenharmony_ci						  &pdd->attr_cu_occupancy);
9778c2ecf20Sopenharmony_ci			kobject_del(pdd->kobj_stats);
9788c2ecf20Sopenharmony_ci			kobject_put(pdd->kobj_stats);
9798c2ecf20Sopenharmony_ci			pdd->kobj_stats = NULL;
9808c2ecf20Sopenharmony_ci		}
9818c2ecf20Sopenharmony_ci
9828c2ecf20Sopenharmony_ci		kobject_del(p->kobj);
9838c2ecf20Sopenharmony_ci		kobject_put(p->kobj);
9848c2ecf20Sopenharmony_ci		p->kobj = NULL;
9858c2ecf20Sopenharmony_ci	}
9868c2ecf20Sopenharmony_ci
9878c2ecf20Sopenharmony_ci	kfd_iommu_unbind_process(p);
9888c2ecf20Sopenharmony_ci
9898c2ecf20Sopenharmony_ci	kfd_process_free_outstanding_kfd_bos(p);
9908c2ecf20Sopenharmony_ci
9918c2ecf20Sopenharmony_ci	kfd_process_destroy_pdds(p);
9928c2ecf20Sopenharmony_ci	dma_fence_put(p->ef);
9938c2ecf20Sopenharmony_ci
9948c2ecf20Sopenharmony_ci	kfd_event_free_process(p);
9958c2ecf20Sopenharmony_ci
9968c2ecf20Sopenharmony_ci	kfd_pasid_free(p->pasid);
9978c2ecf20Sopenharmony_ci	mutex_destroy(&p->mutex);
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_ci	put_task_struct(p->lead_thread);
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_ci	kfree(p);
10028c2ecf20Sopenharmony_ci}
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_cistatic void kfd_process_ref_release(struct kref *ref)
10058c2ecf20Sopenharmony_ci{
10068c2ecf20Sopenharmony_ci	struct kfd_process *p = container_of(ref, struct kfd_process, ref);
10078c2ecf20Sopenharmony_ci
10088c2ecf20Sopenharmony_ci	INIT_WORK(&p->release_work, kfd_process_wq_release);
10098c2ecf20Sopenharmony_ci	queue_work(kfd_process_wq, &p->release_work);
10108c2ecf20Sopenharmony_ci}
10118c2ecf20Sopenharmony_ci
10128c2ecf20Sopenharmony_cistatic void kfd_process_free_notifier(struct mmu_notifier *mn)
10138c2ecf20Sopenharmony_ci{
10148c2ecf20Sopenharmony_ci	kfd_unref_process(container_of(mn, struct kfd_process, mmu_notifier));
10158c2ecf20Sopenharmony_ci}
10168c2ecf20Sopenharmony_ci
10178c2ecf20Sopenharmony_cistatic void kfd_process_notifier_release(struct mmu_notifier *mn,
10188c2ecf20Sopenharmony_ci					struct mm_struct *mm)
10198c2ecf20Sopenharmony_ci{
10208c2ecf20Sopenharmony_ci	struct kfd_process *p;
10218c2ecf20Sopenharmony_ci	struct kfd_process_device *pdd = NULL;
10228c2ecf20Sopenharmony_ci
10238c2ecf20Sopenharmony_ci	/*
10248c2ecf20Sopenharmony_ci	 * The kfd_process structure can not be free because the
10258c2ecf20Sopenharmony_ci	 * mmu_notifier srcu is read locked
10268c2ecf20Sopenharmony_ci	 */
10278c2ecf20Sopenharmony_ci	p = container_of(mn, struct kfd_process, mmu_notifier);
10288c2ecf20Sopenharmony_ci	if (WARN_ON(p->mm != mm))
10298c2ecf20Sopenharmony_ci		return;
10308c2ecf20Sopenharmony_ci
10318c2ecf20Sopenharmony_ci	mutex_lock(&kfd_processes_mutex);
10328c2ecf20Sopenharmony_ci	hash_del_rcu(&p->kfd_processes);
10338c2ecf20Sopenharmony_ci	mutex_unlock(&kfd_processes_mutex);
10348c2ecf20Sopenharmony_ci	synchronize_srcu(&kfd_processes_srcu);
10358c2ecf20Sopenharmony_ci
10368c2ecf20Sopenharmony_ci	cancel_delayed_work_sync(&p->eviction_work);
10378c2ecf20Sopenharmony_ci	cancel_delayed_work_sync(&p->restore_work);
10388c2ecf20Sopenharmony_ci
10398c2ecf20Sopenharmony_ci	mutex_lock(&p->mutex);
10408c2ecf20Sopenharmony_ci
10418c2ecf20Sopenharmony_ci	/* Iterate over all process device data structures and if the
10428c2ecf20Sopenharmony_ci	 * pdd is in debug mode, we should first force unregistration,
10438c2ecf20Sopenharmony_ci	 * then we will be able to destroy the queues
10448c2ecf20Sopenharmony_ci	 */
10458c2ecf20Sopenharmony_ci	list_for_each_entry(pdd, &p->per_device_data, per_device_list) {
10468c2ecf20Sopenharmony_ci		struct kfd_dev *dev = pdd->dev;
10478c2ecf20Sopenharmony_ci
10488c2ecf20Sopenharmony_ci		mutex_lock(kfd_get_dbgmgr_mutex());
10498c2ecf20Sopenharmony_ci		if (dev && dev->dbgmgr && dev->dbgmgr->pasid == p->pasid) {
10508c2ecf20Sopenharmony_ci			if (!kfd_dbgmgr_unregister(dev->dbgmgr, p)) {
10518c2ecf20Sopenharmony_ci				kfd_dbgmgr_destroy(dev->dbgmgr);
10528c2ecf20Sopenharmony_ci				dev->dbgmgr = NULL;
10538c2ecf20Sopenharmony_ci			}
10548c2ecf20Sopenharmony_ci		}
10558c2ecf20Sopenharmony_ci		mutex_unlock(kfd_get_dbgmgr_mutex());
10568c2ecf20Sopenharmony_ci	}
10578c2ecf20Sopenharmony_ci
10588c2ecf20Sopenharmony_ci	kfd_process_dequeue_from_all_devices(p);
10598c2ecf20Sopenharmony_ci	pqm_uninit(&p->pqm);
10608c2ecf20Sopenharmony_ci
10618c2ecf20Sopenharmony_ci	/* Indicate to other users that MM is no longer valid */
10628c2ecf20Sopenharmony_ci	p->mm = NULL;
10638c2ecf20Sopenharmony_ci	/* Signal the eviction fence after user mode queues are
10648c2ecf20Sopenharmony_ci	 * destroyed. This allows any BOs to be freed without
10658c2ecf20Sopenharmony_ci	 * triggering pointless evictions or waiting for fences.
10668c2ecf20Sopenharmony_ci	 */
10678c2ecf20Sopenharmony_ci	dma_fence_signal(p->ef);
10688c2ecf20Sopenharmony_ci
10698c2ecf20Sopenharmony_ci	mutex_unlock(&p->mutex);
10708c2ecf20Sopenharmony_ci
10718c2ecf20Sopenharmony_ci	mmu_notifier_put(&p->mmu_notifier);
10728c2ecf20Sopenharmony_ci}
10738c2ecf20Sopenharmony_ci
10748c2ecf20Sopenharmony_cistatic const struct mmu_notifier_ops kfd_process_mmu_notifier_ops = {
10758c2ecf20Sopenharmony_ci	.release = kfd_process_notifier_release,
10768c2ecf20Sopenharmony_ci	.free_notifier = kfd_process_free_notifier,
10778c2ecf20Sopenharmony_ci};
10788c2ecf20Sopenharmony_ci
10798c2ecf20Sopenharmony_cistatic int kfd_process_init_cwsr_apu(struct kfd_process *p, struct file *filep)
10808c2ecf20Sopenharmony_ci{
10818c2ecf20Sopenharmony_ci	unsigned long  offset;
10828c2ecf20Sopenharmony_ci	struct kfd_process_device *pdd;
10838c2ecf20Sopenharmony_ci
10848c2ecf20Sopenharmony_ci	list_for_each_entry(pdd, &p->per_device_data, per_device_list) {
10858c2ecf20Sopenharmony_ci		struct kfd_dev *dev = pdd->dev;
10868c2ecf20Sopenharmony_ci		struct qcm_process_device *qpd = &pdd->qpd;
10878c2ecf20Sopenharmony_ci
10888c2ecf20Sopenharmony_ci		if (!dev->cwsr_enabled || qpd->cwsr_kaddr || qpd->cwsr_base)
10898c2ecf20Sopenharmony_ci			continue;
10908c2ecf20Sopenharmony_ci
10918c2ecf20Sopenharmony_ci		offset = KFD_MMAP_TYPE_RESERVED_MEM | KFD_MMAP_GPU_ID(dev->id);
10928c2ecf20Sopenharmony_ci		qpd->tba_addr = (int64_t)vm_mmap(filep, 0,
10938c2ecf20Sopenharmony_ci			KFD_CWSR_TBA_TMA_SIZE, PROT_READ | PROT_EXEC,
10948c2ecf20Sopenharmony_ci			MAP_SHARED, offset);
10958c2ecf20Sopenharmony_ci
10968c2ecf20Sopenharmony_ci		if (IS_ERR_VALUE(qpd->tba_addr)) {
10978c2ecf20Sopenharmony_ci			int err = qpd->tba_addr;
10988c2ecf20Sopenharmony_ci
10998c2ecf20Sopenharmony_ci			pr_err("Failure to set tba address. error %d.\n", err);
11008c2ecf20Sopenharmony_ci			qpd->tba_addr = 0;
11018c2ecf20Sopenharmony_ci			qpd->cwsr_kaddr = NULL;
11028c2ecf20Sopenharmony_ci			return err;
11038c2ecf20Sopenharmony_ci		}
11048c2ecf20Sopenharmony_ci
11058c2ecf20Sopenharmony_ci		memcpy(qpd->cwsr_kaddr, dev->cwsr_isa, dev->cwsr_isa_size);
11068c2ecf20Sopenharmony_ci
11078c2ecf20Sopenharmony_ci		qpd->tma_addr = qpd->tba_addr + KFD_CWSR_TMA_OFFSET;
11088c2ecf20Sopenharmony_ci		pr_debug("set tba :0x%llx, tma:0x%llx, cwsr_kaddr:%p for pqm.\n",
11098c2ecf20Sopenharmony_ci			qpd->tba_addr, qpd->tma_addr, qpd->cwsr_kaddr);
11108c2ecf20Sopenharmony_ci	}
11118c2ecf20Sopenharmony_ci
11128c2ecf20Sopenharmony_ci	return 0;
11138c2ecf20Sopenharmony_ci}
11148c2ecf20Sopenharmony_ci
11158c2ecf20Sopenharmony_cistatic int kfd_process_device_init_cwsr_dgpu(struct kfd_process_device *pdd)
11168c2ecf20Sopenharmony_ci{
11178c2ecf20Sopenharmony_ci	struct kfd_dev *dev = pdd->dev;
11188c2ecf20Sopenharmony_ci	struct qcm_process_device *qpd = &pdd->qpd;
11198c2ecf20Sopenharmony_ci	uint32_t flags = KFD_IOC_ALLOC_MEM_FLAGS_GTT
11208c2ecf20Sopenharmony_ci			| KFD_IOC_ALLOC_MEM_FLAGS_NO_SUBSTITUTE
11218c2ecf20Sopenharmony_ci			| KFD_IOC_ALLOC_MEM_FLAGS_EXECUTABLE;
11228c2ecf20Sopenharmony_ci	void *kaddr;
11238c2ecf20Sopenharmony_ci	int ret;
11248c2ecf20Sopenharmony_ci
11258c2ecf20Sopenharmony_ci	if (!dev->cwsr_enabled || qpd->cwsr_kaddr || !qpd->cwsr_base)
11268c2ecf20Sopenharmony_ci		return 0;
11278c2ecf20Sopenharmony_ci
11288c2ecf20Sopenharmony_ci	/* cwsr_base is only set for dGPU */
11298c2ecf20Sopenharmony_ci	ret = kfd_process_alloc_gpuvm(pdd, qpd->cwsr_base,
11308c2ecf20Sopenharmony_ci				      KFD_CWSR_TBA_TMA_SIZE, flags, &kaddr);
11318c2ecf20Sopenharmony_ci	if (ret)
11328c2ecf20Sopenharmony_ci		return ret;
11338c2ecf20Sopenharmony_ci
11348c2ecf20Sopenharmony_ci	qpd->cwsr_kaddr = kaddr;
11358c2ecf20Sopenharmony_ci	qpd->tba_addr = qpd->cwsr_base;
11368c2ecf20Sopenharmony_ci
11378c2ecf20Sopenharmony_ci	memcpy(qpd->cwsr_kaddr, dev->cwsr_isa, dev->cwsr_isa_size);
11388c2ecf20Sopenharmony_ci
11398c2ecf20Sopenharmony_ci	qpd->tma_addr = qpd->tba_addr + KFD_CWSR_TMA_OFFSET;
11408c2ecf20Sopenharmony_ci	pr_debug("set tba :0x%llx, tma:0x%llx, cwsr_kaddr:%p for pqm.\n",
11418c2ecf20Sopenharmony_ci		 qpd->tba_addr, qpd->tma_addr, qpd->cwsr_kaddr);
11428c2ecf20Sopenharmony_ci
11438c2ecf20Sopenharmony_ci	return 0;
11448c2ecf20Sopenharmony_ci}
11458c2ecf20Sopenharmony_ci
11468c2ecf20Sopenharmony_ci/*
11478c2ecf20Sopenharmony_ci * On return the kfd_process is fully operational and will be freed when the
11488c2ecf20Sopenharmony_ci * mm is released
11498c2ecf20Sopenharmony_ci */
11508c2ecf20Sopenharmony_cistatic struct kfd_process *create_process(const struct task_struct *thread)
11518c2ecf20Sopenharmony_ci{
11528c2ecf20Sopenharmony_ci	struct kfd_process *process;
11538c2ecf20Sopenharmony_ci	int err = -ENOMEM;
11548c2ecf20Sopenharmony_ci
11558c2ecf20Sopenharmony_ci	process = kzalloc(sizeof(*process), GFP_KERNEL);
11568c2ecf20Sopenharmony_ci	if (!process)
11578c2ecf20Sopenharmony_ci		goto err_alloc_process;
11588c2ecf20Sopenharmony_ci
11598c2ecf20Sopenharmony_ci	kref_init(&process->ref);
11608c2ecf20Sopenharmony_ci	mutex_init(&process->mutex);
11618c2ecf20Sopenharmony_ci	process->mm = thread->mm;
11628c2ecf20Sopenharmony_ci	process->lead_thread = thread->group_leader;
11638c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&process->per_device_data);
11648c2ecf20Sopenharmony_ci	INIT_DELAYED_WORK(&process->eviction_work, evict_process_worker);
11658c2ecf20Sopenharmony_ci	INIT_DELAYED_WORK(&process->restore_work, restore_process_worker);
11668c2ecf20Sopenharmony_ci	process->last_restore_timestamp = get_jiffies_64();
11678c2ecf20Sopenharmony_ci	kfd_event_init_process(process);
11688c2ecf20Sopenharmony_ci	process->is_32bit_user_mode = in_compat_syscall();
11698c2ecf20Sopenharmony_ci
11708c2ecf20Sopenharmony_ci	process->pasid = kfd_pasid_alloc();
11718c2ecf20Sopenharmony_ci	if (process->pasid == 0)
11728c2ecf20Sopenharmony_ci		goto err_alloc_pasid;
11738c2ecf20Sopenharmony_ci
11748c2ecf20Sopenharmony_ci	err = pqm_init(&process->pqm, process);
11758c2ecf20Sopenharmony_ci	if (err != 0)
11768c2ecf20Sopenharmony_ci		goto err_process_pqm_init;
11778c2ecf20Sopenharmony_ci
11788c2ecf20Sopenharmony_ci	/* init process apertures*/
11798c2ecf20Sopenharmony_ci	err = kfd_init_apertures(process);
11808c2ecf20Sopenharmony_ci	if (err != 0)
11818c2ecf20Sopenharmony_ci		goto err_init_apertures;
11828c2ecf20Sopenharmony_ci
11838c2ecf20Sopenharmony_ci	/* Must be last, have to use release destruction after this */
11848c2ecf20Sopenharmony_ci	process->mmu_notifier.ops = &kfd_process_mmu_notifier_ops;
11858c2ecf20Sopenharmony_ci	err = mmu_notifier_register(&process->mmu_notifier, process->mm);
11868c2ecf20Sopenharmony_ci	if (err)
11878c2ecf20Sopenharmony_ci		goto err_register_notifier;
11888c2ecf20Sopenharmony_ci
11898c2ecf20Sopenharmony_ci	get_task_struct(process->lead_thread);
11908c2ecf20Sopenharmony_ci	hash_add_rcu(kfd_processes_table, &process->kfd_processes,
11918c2ecf20Sopenharmony_ci			(uintptr_t)process->mm);
11928c2ecf20Sopenharmony_ci
11938c2ecf20Sopenharmony_ci	return process;
11948c2ecf20Sopenharmony_ci
11958c2ecf20Sopenharmony_cierr_register_notifier:
11968c2ecf20Sopenharmony_ci	kfd_process_free_outstanding_kfd_bos(process);
11978c2ecf20Sopenharmony_ci	kfd_process_destroy_pdds(process);
11988c2ecf20Sopenharmony_cierr_init_apertures:
11998c2ecf20Sopenharmony_ci	pqm_uninit(&process->pqm);
12008c2ecf20Sopenharmony_cierr_process_pqm_init:
12018c2ecf20Sopenharmony_ci	kfd_pasid_free(process->pasid);
12028c2ecf20Sopenharmony_cierr_alloc_pasid:
12038c2ecf20Sopenharmony_ci	mutex_destroy(&process->mutex);
12048c2ecf20Sopenharmony_ci	kfree(process);
12058c2ecf20Sopenharmony_cierr_alloc_process:
12068c2ecf20Sopenharmony_ci	return ERR_PTR(err);
12078c2ecf20Sopenharmony_ci}
12088c2ecf20Sopenharmony_ci
12098c2ecf20Sopenharmony_cistatic int init_doorbell_bitmap(struct qcm_process_device *qpd,
12108c2ecf20Sopenharmony_ci			struct kfd_dev *dev)
12118c2ecf20Sopenharmony_ci{
12128c2ecf20Sopenharmony_ci	unsigned int i;
12138c2ecf20Sopenharmony_ci	int range_start = dev->shared_resources.non_cp_doorbells_start;
12148c2ecf20Sopenharmony_ci	int range_end = dev->shared_resources.non_cp_doorbells_end;
12158c2ecf20Sopenharmony_ci
12168c2ecf20Sopenharmony_ci	if (!KFD_IS_SOC15(dev->device_info->asic_family))
12178c2ecf20Sopenharmony_ci		return 0;
12188c2ecf20Sopenharmony_ci
12198c2ecf20Sopenharmony_ci	qpd->doorbell_bitmap =
12208c2ecf20Sopenharmony_ci		kzalloc(DIV_ROUND_UP(KFD_MAX_NUM_OF_QUEUES_PER_PROCESS,
12218c2ecf20Sopenharmony_ci				     BITS_PER_BYTE), GFP_KERNEL);
12228c2ecf20Sopenharmony_ci	if (!qpd->doorbell_bitmap)
12238c2ecf20Sopenharmony_ci		return -ENOMEM;
12248c2ecf20Sopenharmony_ci
12258c2ecf20Sopenharmony_ci	/* Mask out doorbells reserved for SDMA, IH, and VCN on SOC15. */
12268c2ecf20Sopenharmony_ci	pr_debug("reserved doorbell 0x%03x - 0x%03x\n", range_start, range_end);
12278c2ecf20Sopenharmony_ci	pr_debug("reserved doorbell 0x%03x - 0x%03x\n",
12288c2ecf20Sopenharmony_ci			range_start + KFD_QUEUE_DOORBELL_MIRROR_OFFSET,
12298c2ecf20Sopenharmony_ci			range_end + KFD_QUEUE_DOORBELL_MIRROR_OFFSET);
12308c2ecf20Sopenharmony_ci
12318c2ecf20Sopenharmony_ci	for (i = 0; i < KFD_MAX_NUM_OF_QUEUES_PER_PROCESS / 2; i++) {
12328c2ecf20Sopenharmony_ci		if (i >= range_start && i <= range_end) {
12338c2ecf20Sopenharmony_ci			set_bit(i, qpd->doorbell_bitmap);
12348c2ecf20Sopenharmony_ci			set_bit(i + KFD_QUEUE_DOORBELL_MIRROR_OFFSET,
12358c2ecf20Sopenharmony_ci				qpd->doorbell_bitmap);
12368c2ecf20Sopenharmony_ci		}
12378c2ecf20Sopenharmony_ci	}
12388c2ecf20Sopenharmony_ci
12398c2ecf20Sopenharmony_ci	return 0;
12408c2ecf20Sopenharmony_ci}
12418c2ecf20Sopenharmony_ci
12428c2ecf20Sopenharmony_cistruct kfd_process_device *kfd_get_process_device_data(struct kfd_dev *dev,
12438c2ecf20Sopenharmony_ci							struct kfd_process *p)
12448c2ecf20Sopenharmony_ci{
12458c2ecf20Sopenharmony_ci	struct kfd_process_device *pdd = NULL;
12468c2ecf20Sopenharmony_ci
12478c2ecf20Sopenharmony_ci	list_for_each_entry(pdd, &p->per_device_data, per_device_list)
12488c2ecf20Sopenharmony_ci		if (pdd->dev == dev)
12498c2ecf20Sopenharmony_ci			return pdd;
12508c2ecf20Sopenharmony_ci
12518c2ecf20Sopenharmony_ci	return NULL;
12528c2ecf20Sopenharmony_ci}
12538c2ecf20Sopenharmony_ci
12548c2ecf20Sopenharmony_cistruct kfd_process_device *kfd_create_process_device_data(struct kfd_dev *dev,
12558c2ecf20Sopenharmony_ci							struct kfd_process *p)
12568c2ecf20Sopenharmony_ci{
12578c2ecf20Sopenharmony_ci	struct kfd_process_device *pdd = NULL;
12588c2ecf20Sopenharmony_ci
12598c2ecf20Sopenharmony_ci	pdd = kzalloc(sizeof(*pdd), GFP_KERNEL);
12608c2ecf20Sopenharmony_ci	if (!pdd)
12618c2ecf20Sopenharmony_ci		return NULL;
12628c2ecf20Sopenharmony_ci
12638c2ecf20Sopenharmony_ci	if (kfd_alloc_process_doorbells(dev, &pdd->doorbell_index) < 0) {
12648c2ecf20Sopenharmony_ci		pr_err("Failed to alloc doorbell for pdd\n");
12658c2ecf20Sopenharmony_ci		goto err_free_pdd;
12668c2ecf20Sopenharmony_ci	}
12678c2ecf20Sopenharmony_ci
12688c2ecf20Sopenharmony_ci	if (init_doorbell_bitmap(&pdd->qpd, dev)) {
12698c2ecf20Sopenharmony_ci		pr_err("Failed to init doorbell for process\n");
12708c2ecf20Sopenharmony_ci		goto err_free_pdd;
12718c2ecf20Sopenharmony_ci	}
12728c2ecf20Sopenharmony_ci
12738c2ecf20Sopenharmony_ci	pdd->dev = dev;
12748c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&pdd->qpd.queues_list);
12758c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&pdd->qpd.priv_queue_list);
12768c2ecf20Sopenharmony_ci	pdd->qpd.dqm = dev->dqm;
12778c2ecf20Sopenharmony_ci	pdd->qpd.pqm = &p->pqm;
12788c2ecf20Sopenharmony_ci	pdd->qpd.evicted = 0;
12798c2ecf20Sopenharmony_ci	pdd->qpd.mapped_gws_queue = false;
12808c2ecf20Sopenharmony_ci	pdd->process = p;
12818c2ecf20Sopenharmony_ci	pdd->bound = PDD_UNBOUND;
12828c2ecf20Sopenharmony_ci	pdd->already_dequeued = false;
12838c2ecf20Sopenharmony_ci	pdd->runtime_inuse = false;
12848c2ecf20Sopenharmony_ci	pdd->vram_usage = 0;
12858c2ecf20Sopenharmony_ci	pdd->sdma_past_activity_counter = 0;
12868c2ecf20Sopenharmony_ci	atomic64_set(&pdd->evict_duration_counter, 0);
12878c2ecf20Sopenharmony_ci	list_add(&pdd->per_device_list, &p->per_device_data);
12888c2ecf20Sopenharmony_ci
12898c2ecf20Sopenharmony_ci	/* Init idr used for memory handle translation */
12908c2ecf20Sopenharmony_ci	idr_init(&pdd->alloc_idr);
12918c2ecf20Sopenharmony_ci
12928c2ecf20Sopenharmony_ci	return pdd;
12938c2ecf20Sopenharmony_ci
12948c2ecf20Sopenharmony_cierr_free_pdd:
12958c2ecf20Sopenharmony_ci	kfree(pdd);
12968c2ecf20Sopenharmony_ci	return NULL;
12978c2ecf20Sopenharmony_ci}
12988c2ecf20Sopenharmony_ci
12998c2ecf20Sopenharmony_ci/**
13008c2ecf20Sopenharmony_ci * kfd_process_device_init_vm - Initialize a VM for a process-device
13018c2ecf20Sopenharmony_ci *
13028c2ecf20Sopenharmony_ci * @pdd: The process-device
13038c2ecf20Sopenharmony_ci * @drm_file: Optional pointer to a DRM file descriptor
13048c2ecf20Sopenharmony_ci *
13058c2ecf20Sopenharmony_ci * If @drm_file is specified, it will be used to acquire the VM from
13068c2ecf20Sopenharmony_ci * that file descriptor. If successful, the @pdd takes ownership of
13078c2ecf20Sopenharmony_ci * the file descriptor.
13088c2ecf20Sopenharmony_ci *
13098c2ecf20Sopenharmony_ci * If @drm_file is NULL, a new VM is created.
13108c2ecf20Sopenharmony_ci *
13118c2ecf20Sopenharmony_ci * Returns 0 on success, -errno on failure.
13128c2ecf20Sopenharmony_ci */
13138c2ecf20Sopenharmony_ciint kfd_process_device_init_vm(struct kfd_process_device *pdd,
13148c2ecf20Sopenharmony_ci			       struct file *drm_file)
13158c2ecf20Sopenharmony_ci{
13168c2ecf20Sopenharmony_ci	struct kfd_process *p;
13178c2ecf20Sopenharmony_ci	struct kfd_dev *dev;
13188c2ecf20Sopenharmony_ci	int ret;
13198c2ecf20Sopenharmony_ci
13208c2ecf20Sopenharmony_ci	if (pdd->vm)
13218c2ecf20Sopenharmony_ci		return drm_file ? -EBUSY : 0;
13228c2ecf20Sopenharmony_ci
13238c2ecf20Sopenharmony_ci	p = pdd->process;
13248c2ecf20Sopenharmony_ci	dev = pdd->dev;
13258c2ecf20Sopenharmony_ci
13268c2ecf20Sopenharmony_ci	if (drm_file)
13278c2ecf20Sopenharmony_ci		ret = amdgpu_amdkfd_gpuvm_acquire_process_vm(
13288c2ecf20Sopenharmony_ci			dev->kgd, drm_file, p->pasid,
13298c2ecf20Sopenharmony_ci			&pdd->vm, &p->kgd_process_info, &p->ef);
13308c2ecf20Sopenharmony_ci	else
13318c2ecf20Sopenharmony_ci		ret = amdgpu_amdkfd_gpuvm_create_process_vm(dev->kgd, p->pasid,
13328c2ecf20Sopenharmony_ci			&pdd->vm, &p->kgd_process_info, &p->ef);
13338c2ecf20Sopenharmony_ci	if (ret) {
13348c2ecf20Sopenharmony_ci		pr_err("Failed to create process VM object\n");
13358c2ecf20Sopenharmony_ci		return ret;
13368c2ecf20Sopenharmony_ci	}
13378c2ecf20Sopenharmony_ci
13388c2ecf20Sopenharmony_ci	amdgpu_vm_set_task_info(pdd->vm);
13398c2ecf20Sopenharmony_ci
13408c2ecf20Sopenharmony_ci	ret = kfd_process_device_reserve_ib_mem(pdd);
13418c2ecf20Sopenharmony_ci	if (ret)
13428c2ecf20Sopenharmony_ci		goto err_reserve_ib_mem;
13438c2ecf20Sopenharmony_ci	ret = kfd_process_device_init_cwsr_dgpu(pdd);
13448c2ecf20Sopenharmony_ci	if (ret)
13458c2ecf20Sopenharmony_ci		goto err_init_cwsr;
13468c2ecf20Sopenharmony_ci
13478c2ecf20Sopenharmony_ci	pdd->drm_file = drm_file;
13488c2ecf20Sopenharmony_ci
13498c2ecf20Sopenharmony_ci	return 0;
13508c2ecf20Sopenharmony_ci
13518c2ecf20Sopenharmony_cierr_init_cwsr:
13528c2ecf20Sopenharmony_cierr_reserve_ib_mem:
13538c2ecf20Sopenharmony_ci	kfd_process_device_free_bos(pdd);
13548c2ecf20Sopenharmony_ci	if (!drm_file)
13558c2ecf20Sopenharmony_ci		amdgpu_amdkfd_gpuvm_destroy_process_vm(dev->kgd, pdd->vm);
13568c2ecf20Sopenharmony_ci	pdd->vm = NULL;
13578c2ecf20Sopenharmony_ci
13588c2ecf20Sopenharmony_ci	return ret;
13598c2ecf20Sopenharmony_ci}
13608c2ecf20Sopenharmony_ci
13618c2ecf20Sopenharmony_ci/*
13628c2ecf20Sopenharmony_ci * Direct the IOMMU to bind the process (specifically the pasid->mm)
13638c2ecf20Sopenharmony_ci * to the device.
13648c2ecf20Sopenharmony_ci * Unbinding occurs when the process dies or the device is removed.
13658c2ecf20Sopenharmony_ci *
13668c2ecf20Sopenharmony_ci * Assumes that the process lock is held.
13678c2ecf20Sopenharmony_ci */
13688c2ecf20Sopenharmony_cistruct kfd_process_device *kfd_bind_process_to_device(struct kfd_dev *dev,
13698c2ecf20Sopenharmony_ci							struct kfd_process *p)
13708c2ecf20Sopenharmony_ci{
13718c2ecf20Sopenharmony_ci	struct kfd_process_device *pdd;
13728c2ecf20Sopenharmony_ci	int err;
13738c2ecf20Sopenharmony_ci
13748c2ecf20Sopenharmony_ci	pdd = kfd_get_process_device_data(dev, p);
13758c2ecf20Sopenharmony_ci	if (!pdd) {
13768c2ecf20Sopenharmony_ci		pr_err("Process device data doesn't exist\n");
13778c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
13788c2ecf20Sopenharmony_ci	}
13798c2ecf20Sopenharmony_ci
13808c2ecf20Sopenharmony_ci	/*
13818c2ecf20Sopenharmony_ci	 * signal runtime-pm system to auto resume and prevent
13828c2ecf20Sopenharmony_ci	 * further runtime suspend once device pdd is created until
13838c2ecf20Sopenharmony_ci	 * pdd is destroyed.
13848c2ecf20Sopenharmony_ci	 */
13858c2ecf20Sopenharmony_ci	if (!pdd->runtime_inuse) {
13868c2ecf20Sopenharmony_ci		err = pm_runtime_get_sync(dev->ddev->dev);
13878c2ecf20Sopenharmony_ci		if (err < 0) {
13888c2ecf20Sopenharmony_ci			pm_runtime_put_autosuspend(dev->ddev->dev);
13898c2ecf20Sopenharmony_ci			return ERR_PTR(err);
13908c2ecf20Sopenharmony_ci		}
13918c2ecf20Sopenharmony_ci	}
13928c2ecf20Sopenharmony_ci
13938c2ecf20Sopenharmony_ci	err = kfd_iommu_bind_process_to_device(pdd);
13948c2ecf20Sopenharmony_ci	if (err)
13958c2ecf20Sopenharmony_ci		goto out;
13968c2ecf20Sopenharmony_ci
13978c2ecf20Sopenharmony_ci	err = kfd_process_device_init_vm(pdd, NULL);
13988c2ecf20Sopenharmony_ci	if (err)
13998c2ecf20Sopenharmony_ci		goto out;
14008c2ecf20Sopenharmony_ci
14018c2ecf20Sopenharmony_ci	/*
14028c2ecf20Sopenharmony_ci	 * make sure that runtime_usage counter is incremented just once
14038c2ecf20Sopenharmony_ci	 * per pdd
14048c2ecf20Sopenharmony_ci	 */
14058c2ecf20Sopenharmony_ci	pdd->runtime_inuse = true;
14068c2ecf20Sopenharmony_ci
14078c2ecf20Sopenharmony_ci	return pdd;
14088c2ecf20Sopenharmony_ci
14098c2ecf20Sopenharmony_ciout:
14108c2ecf20Sopenharmony_ci	/* balance runpm reference count and exit with error */
14118c2ecf20Sopenharmony_ci	if (!pdd->runtime_inuse) {
14128c2ecf20Sopenharmony_ci		pm_runtime_mark_last_busy(dev->ddev->dev);
14138c2ecf20Sopenharmony_ci		pm_runtime_put_autosuspend(dev->ddev->dev);
14148c2ecf20Sopenharmony_ci	}
14158c2ecf20Sopenharmony_ci
14168c2ecf20Sopenharmony_ci	return ERR_PTR(err);
14178c2ecf20Sopenharmony_ci}
14188c2ecf20Sopenharmony_ci
14198c2ecf20Sopenharmony_cistruct kfd_process_device *kfd_get_first_process_device_data(
14208c2ecf20Sopenharmony_ci						struct kfd_process *p)
14218c2ecf20Sopenharmony_ci{
14228c2ecf20Sopenharmony_ci	return list_first_entry(&p->per_device_data,
14238c2ecf20Sopenharmony_ci				struct kfd_process_device,
14248c2ecf20Sopenharmony_ci				per_device_list);
14258c2ecf20Sopenharmony_ci}
14268c2ecf20Sopenharmony_ci
14278c2ecf20Sopenharmony_cistruct kfd_process_device *kfd_get_next_process_device_data(
14288c2ecf20Sopenharmony_ci						struct kfd_process *p,
14298c2ecf20Sopenharmony_ci						struct kfd_process_device *pdd)
14308c2ecf20Sopenharmony_ci{
14318c2ecf20Sopenharmony_ci	if (list_is_last(&pdd->per_device_list, &p->per_device_data))
14328c2ecf20Sopenharmony_ci		return NULL;
14338c2ecf20Sopenharmony_ci	return list_next_entry(pdd, per_device_list);
14348c2ecf20Sopenharmony_ci}
14358c2ecf20Sopenharmony_ci
14368c2ecf20Sopenharmony_cibool kfd_has_process_device_data(struct kfd_process *p)
14378c2ecf20Sopenharmony_ci{
14388c2ecf20Sopenharmony_ci	return !(list_empty(&p->per_device_data));
14398c2ecf20Sopenharmony_ci}
14408c2ecf20Sopenharmony_ci
14418c2ecf20Sopenharmony_ci/* Create specific handle mapped to mem from process local memory idr
14428c2ecf20Sopenharmony_ci * Assumes that the process lock is held.
14438c2ecf20Sopenharmony_ci */
14448c2ecf20Sopenharmony_ciint kfd_process_device_create_obj_handle(struct kfd_process_device *pdd,
14458c2ecf20Sopenharmony_ci					void *mem)
14468c2ecf20Sopenharmony_ci{
14478c2ecf20Sopenharmony_ci	return idr_alloc(&pdd->alloc_idr, mem, 0, 0, GFP_KERNEL);
14488c2ecf20Sopenharmony_ci}
14498c2ecf20Sopenharmony_ci
14508c2ecf20Sopenharmony_ci/* Translate specific handle from process local memory idr
14518c2ecf20Sopenharmony_ci * Assumes that the process lock is held.
14528c2ecf20Sopenharmony_ci */
14538c2ecf20Sopenharmony_civoid *kfd_process_device_translate_handle(struct kfd_process_device *pdd,
14548c2ecf20Sopenharmony_ci					int handle)
14558c2ecf20Sopenharmony_ci{
14568c2ecf20Sopenharmony_ci	if (handle < 0)
14578c2ecf20Sopenharmony_ci		return NULL;
14588c2ecf20Sopenharmony_ci
14598c2ecf20Sopenharmony_ci	return idr_find(&pdd->alloc_idr, handle);
14608c2ecf20Sopenharmony_ci}
14618c2ecf20Sopenharmony_ci
14628c2ecf20Sopenharmony_ci/* Remove specific handle from process local memory idr
14638c2ecf20Sopenharmony_ci * Assumes that the process lock is held.
14648c2ecf20Sopenharmony_ci */
14658c2ecf20Sopenharmony_civoid kfd_process_device_remove_obj_handle(struct kfd_process_device *pdd,
14668c2ecf20Sopenharmony_ci					int handle)
14678c2ecf20Sopenharmony_ci{
14688c2ecf20Sopenharmony_ci	if (handle >= 0)
14698c2ecf20Sopenharmony_ci		idr_remove(&pdd->alloc_idr, handle);
14708c2ecf20Sopenharmony_ci}
14718c2ecf20Sopenharmony_ci
14728c2ecf20Sopenharmony_ci/* This increments the process->ref counter. */
14738c2ecf20Sopenharmony_cistruct kfd_process *kfd_lookup_process_by_pasid(u32 pasid)
14748c2ecf20Sopenharmony_ci{
14758c2ecf20Sopenharmony_ci	struct kfd_process *p, *ret_p = NULL;
14768c2ecf20Sopenharmony_ci	unsigned int temp;
14778c2ecf20Sopenharmony_ci
14788c2ecf20Sopenharmony_ci	int idx = srcu_read_lock(&kfd_processes_srcu);
14798c2ecf20Sopenharmony_ci
14808c2ecf20Sopenharmony_ci	hash_for_each_rcu(kfd_processes_table, temp, p, kfd_processes) {
14818c2ecf20Sopenharmony_ci		if (p->pasid == pasid) {
14828c2ecf20Sopenharmony_ci			kref_get(&p->ref);
14838c2ecf20Sopenharmony_ci			ret_p = p;
14848c2ecf20Sopenharmony_ci			break;
14858c2ecf20Sopenharmony_ci		}
14868c2ecf20Sopenharmony_ci	}
14878c2ecf20Sopenharmony_ci
14888c2ecf20Sopenharmony_ci	srcu_read_unlock(&kfd_processes_srcu, idx);
14898c2ecf20Sopenharmony_ci
14908c2ecf20Sopenharmony_ci	return ret_p;
14918c2ecf20Sopenharmony_ci}
14928c2ecf20Sopenharmony_ci
14938c2ecf20Sopenharmony_ci/* This increments the process->ref counter. */
14948c2ecf20Sopenharmony_cistruct kfd_process *kfd_lookup_process_by_mm(const struct mm_struct *mm)
14958c2ecf20Sopenharmony_ci{
14968c2ecf20Sopenharmony_ci	struct kfd_process *p;
14978c2ecf20Sopenharmony_ci
14988c2ecf20Sopenharmony_ci	int idx = srcu_read_lock(&kfd_processes_srcu);
14998c2ecf20Sopenharmony_ci
15008c2ecf20Sopenharmony_ci	p = find_process_by_mm(mm);
15018c2ecf20Sopenharmony_ci	if (p)
15028c2ecf20Sopenharmony_ci		kref_get(&p->ref);
15038c2ecf20Sopenharmony_ci
15048c2ecf20Sopenharmony_ci	srcu_read_unlock(&kfd_processes_srcu, idx);
15058c2ecf20Sopenharmony_ci
15068c2ecf20Sopenharmony_ci	return p;
15078c2ecf20Sopenharmony_ci}
15088c2ecf20Sopenharmony_ci
15098c2ecf20Sopenharmony_ci/* kfd_process_evict_queues - Evict all user queues of a process
15108c2ecf20Sopenharmony_ci *
15118c2ecf20Sopenharmony_ci * Eviction is reference-counted per process-device. This means multiple
15128c2ecf20Sopenharmony_ci * evictions from different sources can be nested safely.
15138c2ecf20Sopenharmony_ci */
15148c2ecf20Sopenharmony_ciint kfd_process_evict_queues(struct kfd_process *p)
15158c2ecf20Sopenharmony_ci{
15168c2ecf20Sopenharmony_ci	struct kfd_process_device *pdd;
15178c2ecf20Sopenharmony_ci	int r = 0;
15188c2ecf20Sopenharmony_ci	unsigned int n_evicted = 0;
15198c2ecf20Sopenharmony_ci
15208c2ecf20Sopenharmony_ci	list_for_each_entry(pdd, &p->per_device_data, per_device_list) {
15218c2ecf20Sopenharmony_ci		r = pdd->dev->dqm->ops.evict_process_queues(pdd->dev->dqm,
15228c2ecf20Sopenharmony_ci							    &pdd->qpd);
15238c2ecf20Sopenharmony_ci		if (r) {
15248c2ecf20Sopenharmony_ci			pr_err("Failed to evict process queues\n");
15258c2ecf20Sopenharmony_ci			goto fail;
15268c2ecf20Sopenharmony_ci		}
15278c2ecf20Sopenharmony_ci		n_evicted++;
15288c2ecf20Sopenharmony_ci	}
15298c2ecf20Sopenharmony_ci
15308c2ecf20Sopenharmony_ci	return r;
15318c2ecf20Sopenharmony_ci
15328c2ecf20Sopenharmony_cifail:
15338c2ecf20Sopenharmony_ci	/* To keep state consistent, roll back partial eviction by
15348c2ecf20Sopenharmony_ci	 * restoring queues
15358c2ecf20Sopenharmony_ci	 */
15368c2ecf20Sopenharmony_ci	list_for_each_entry(pdd, &p->per_device_data, per_device_list) {
15378c2ecf20Sopenharmony_ci		if (n_evicted == 0)
15388c2ecf20Sopenharmony_ci			break;
15398c2ecf20Sopenharmony_ci		if (pdd->dev->dqm->ops.restore_process_queues(pdd->dev->dqm,
15408c2ecf20Sopenharmony_ci							      &pdd->qpd))
15418c2ecf20Sopenharmony_ci			pr_err("Failed to restore queues\n");
15428c2ecf20Sopenharmony_ci
15438c2ecf20Sopenharmony_ci		n_evicted--;
15448c2ecf20Sopenharmony_ci	}
15458c2ecf20Sopenharmony_ci
15468c2ecf20Sopenharmony_ci	return r;
15478c2ecf20Sopenharmony_ci}
15488c2ecf20Sopenharmony_ci
15498c2ecf20Sopenharmony_ci/* kfd_process_restore_queues - Restore all user queues of a process */
15508c2ecf20Sopenharmony_ciint kfd_process_restore_queues(struct kfd_process *p)
15518c2ecf20Sopenharmony_ci{
15528c2ecf20Sopenharmony_ci	struct kfd_process_device *pdd;
15538c2ecf20Sopenharmony_ci	int r, ret = 0;
15548c2ecf20Sopenharmony_ci
15558c2ecf20Sopenharmony_ci	list_for_each_entry(pdd, &p->per_device_data, per_device_list) {
15568c2ecf20Sopenharmony_ci		r = pdd->dev->dqm->ops.restore_process_queues(pdd->dev->dqm,
15578c2ecf20Sopenharmony_ci							      &pdd->qpd);
15588c2ecf20Sopenharmony_ci		if (r) {
15598c2ecf20Sopenharmony_ci			pr_err("Failed to restore process queues\n");
15608c2ecf20Sopenharmony_ci			if (!ret)
15618c2ecf20Sopenharmony_ci				ret = r;
15628c2ecf20Sopenharmony_ci		}
15638c2ecf20Sopenharmony_ci	}
15648c2ecf20Sopenharmony_ci
15658c2ecf20Sopenharmony_ci	return ret;
15668c2ecf20Sopenharmony_ci}
15678c2ecf20Sopenharmony_ci
15688c2ecf20Sopenharmony_cistatic void evict_process_worker(struct work_struct *work)
15698c2ecf20Sopenharmony_ci{
15708c2ecf20Sopenharmony_ci	int ret;
15718c2ecf20Sopenharmony_ci	struct kfd_process *p;
15728c2ecf20Sopenharmony_ci	struct delayed_work *dwork;
15738c2ecf20Sopenharmony_ci
15748c2ecf20Sopenharmony_ci	dwork = to_delayed_work(work);
15758c2ecf20Sopenharmony_ci
15768c2ecf20Sopenharmony_ci	/* Process termination destroys this worker thread. So during the
15778c2ecf20Sopenharmony_ci	 * lifetime of this thread, kfd_process p will be valid
15788c2ecf20Sopenharmony_ci	 */
15798c2ecf20Sopenharmony_ci	p = container_of(dwork, struct kfd_process, eviction_work);
15808c2ecf20Sopenharmony_ci	WARN_ONCE(p->last_eviction_seqno != p->ef->seqno,
15818c2ecf20Sopenharmony_ci		  "Eviction fence mismatch\n");
15828c2ecf20Sopenharmony_ci
15838c2ecf20Sopenharmony_ci	/* Narrow window of overlap between restore and evict work
15848c2ecf20Sopenharmony_ci	 * item is possible. Once amdgpu_amdkfd_gpuvm_restore_process_bos
15858c2ecf20Sopenharmony_ci	 * unreserves KFD BOs, it is possible to evicted again. But
15868c2ecf20Sopenharmony_ci	 * restore has few more steps of finish. So lets wait for any
15878c2ecf20Sopenharmony_ci	 * previous restore work to complete
15888c2ecf20Sopenharmony_ci	 */
15898c2ecf20Sopenharmony_ci	flush_delayed_work(&p->restore_work);
15908c2ecf20Sopenharmony_ci
15918c2ecf20Sopenharmony_ci	pr_debug("Started evicting pasid 0x%x\n", p->pasid);
15928c2ecf20Sopenharmony_ci	ret = kfd_process_evict_queues(p);
15938c2ecf20Sopenharmony_ci	if (!ret) {
15948c2ecf20Sopenharmony_ci		dma_fence_signal(p->ef);
15958c2ecf20Sopenharmony_ci		dma_fence_put(p->ef);
15968c2ecf20Sopenharmony_ci		p->ef = NULL;
15978c2ecf20Sopenharmony_ci		queue_delayed_work(kfd_restore_wq, &p->restore_work,
15988c2ecf20Sopenharmony_ci				msecs_to_jiffies(PROCESS_RESTORE_TIME_MS));
15998c2ecf20Sopenharmony_ci
16008c2ecf20Sopenharmony_ci		pr_debug("Finished evicting pasid 0x%x\n", p->pasid);
16018c2ecf20Sopenharmony_ci	} else
16028c2ecf20Sopenharmony_ci		pr_err("Failed to evict queues of pasid 0x%x\n", p->pasid);
16038c2ecf20Sopenharmony_ci}
16048c2ecf20Sopenharmony_ci
16058c2ecf20Sopenharmony_cistatic void restore_process_worker(struct work_struct *work)
16068c2ecf20Sopenharmony_ci{
16078c2ecf20Sopenharmony_ci	struct delayed_work *dwork;
16088c2ecf20Sopenharmony_ci	struct kfd_process *p;
16098c2ecf20Sopenharmony_ci	int ret = 0;
16108c2ecf20Sopenharmony_ci
16118c2ecf20Sopenharmony_ci	dwork = to_delayed_work(work);
16128c2ecf20Sopenharmony_ci
16138c2ecf20Sopenharmony_ci	/* Process termination destroys this worker thread. So during the
16148c2ecf20Sopenharmony_ci	 * lifetime of this thread, kfd_process p will be valid
16158c2ecf20Sopenharmony_ci	 */
16168c2ecf20Sopenharmony_ci	p = container_of(dwork, struct kfd_process, restore_work);
16178c2ecf20Sopenharmony_ci	pr_debug("Started restoring pasid 0x%x\n", p->pasid);
16188c2ecf20Sopenharmony_ci
16198c2ecf20Sopenharmony_ci	/* Setting last_restore_timestamp before successful restoration.
16208c2ecf20Sopenharmony_ci	 * Otherwise this would have to be set by KGD (restore_process_bos)
16218c2ecf20Sopenharmony_ci	 * before KFD BOs are unreserved. If not, the process can be evicted
16228c2ecf20Sopenharmony_ci	 * again before the timestamp is set.
16238c2ecf20Sopenharmony_ci	 * If restore fails, the timestamp will be set again in the next
16248c2ecf20Sopenharmony_ci	 * attempt. This would mean that the minimum GPU quanta would be
16258c2ecf20Sopenharmony_ci	 * PROCESS_ACTIVE_TIME_MS - (time to execute the following two
16268c2ecf20Sopenharmony_ci	 * functions)
16278c2ecf20Sopenharmony_ci	 */
16288c2ecf20Sopenharmony_ci
16298c2ecf20Sopenharmony_ci	p->last_restore_timestamp = get_jiffies_64();
16308c2ecf20Sopenharmony_ci	ret = amdgpu_amdkfd_gpuvm_restore_process_bos(p->kgd_process_info,
16318c2ecf20Sopenharmony_ci						     &p->ef);
16328c2ecf20Sopenharmony_ci	if (ret) {
16338c2ecf20Sopenharmony_ci		pr_debug("Failed to restore BOs of pasid 0x%x, retry after %d ms\n",
16348c2ecf20Sopenharmony_ci			 p->pasid, PROCESS_BACK_OFF_TIME_MS);
16358c2ecf20Sopenharmony_ci		ret = queue_delayed_work(kfd_restore_wq, &p->restore_work,
16368c2ecf20Sopenharmony_ci				msecs_to_jiffies(PROCESS_BACK_OFF_TIME_MS));
16378c2ecf20Sopenharmony_ci		WARN(!ret, "reschedule restore work failed\n");
16388c2ecf20Sopenharmony_ci		return;
16398c2ecf20Sopenharmony_ci	}
16408c2ecf20Sopenharmony_ci
16418c2ecf20Sopenharmony_ci	ret = kfd_process_restore_queues(p);
16428c2ecf20Sopenharmony_ci	if (!ret)
16438c2ecf20Sopenharmony_ci		pr_debug("Finished restoring pasid 0x%x\n", p->pasid);
16448c2ecf20Sopenharmony_ci	else
16458c2ecf20Sopenharmony_ci		pr_err("Failed to restore queues of pasid 0x%x\n", p->pasid);
16468c2ecf20Sopenharmony_ci}
16478c2ecf20Sopenharmony_ci
16488c2ecf20Sopenharmony_civoid kfd_suspend_all_processes(void)
16498c2ecf20Sopenharmony_ci{
16508c2ecf20Sopenharmony_ci	struct kfd_process *p;
16518c2ecf20Sopenharmony_ci	unsigned int temp;
16528c2ecf20Sopenharmony_ci	int idx = srcu_read_lock(&kfd_processes_srcu);
16538c2ecf20Sopenharmony_ci
16548c2ecf20Sopenharmony_ci	WARN(debug_evictions, "Evicting all processes");
16558c2ecf20Sopenharmony_ci	hash_for_each_rcu(kfd_processes_table, temp, p, kfd_processes) {
16568c2ecf20Sopenharmony_ci		cancel_delayed_work_sync(&p->eviction_work);
16578c2ecf20Sopenharmony_ci		cancel_delayed_work_sync(&p->restore_work);
16588c2ecf20Sopenharmony_ci
16598c2ecf20Sopenharmony_ci		if (kfd_process_evict_queues(p))
16608c2ecf20Sopenharmony_ci			pr_err("Failed to suspend process 0x%x\n", p->pasid);
16618c2ecf20Sopenharmony_ci		dma_fence_signal(p->ef);
16628c2ecf20Sopenharmony_ci		dma_fence_put(p->ef);
16638c2ecf20Sopenharmony_ci		p->ef = NULL;
16648c2ecf20Sopenharmony_ci	}
16658c2ecf20Sopenharmony_ci	srcu_read_unlock(&kfd_processes_srcu, idx);
16668c2ecf20Sopenharmony_ci}
16678c2ecf20Sopenharmony_ci
16688c2ecf20Sopenharmony_ciint kfd_resume_all_processes(void)
16698c2ecf20Sopenharmony_ci{
16708c2ecf20Sopenharmony_ci	struct kfd_process *p;
16718c2ecf20Sopenharmony_ci	unsigned int temp;
16728c2ecf20Sopenharmony_ci	int ret = 0, idx = srcu_read_lock(&kfd_processes_srcu);
16738c2ecf20Sopenharmony_ci
16748c2ecf20Sopenharmony_ci	hash_for_each_rcu(kfd_processes_table, temp, p, kfd_processes) {
16758c2ecf20Sopenharmony_ci		if (!queue_delayed_work(kfd_restore_wq, &p->restore_work, 0)) {
16768c2ecf20Sopenharmony_ci			pr_err("Restore process %d failed during resume\n",
16778c2ecf20Sopenharmony_ci			       p->pasid);
16788c2ecf20Sopenharmony_ci			ret = -EFAULT;
16798c2ecf20Sopenharmony_ci		}
16808c2ecf20Sopenharmony_ci	}
16818c2ecf20Sopenharmony_ci	srcu_read_unlock(&kfd_processes_srcu, idx);
16828c2ecf20Sopenharmony_ci	return ret;
16838c2ecf20Sopenharmony_ci}
16848c2ecf20Sopenharmony_ci
16858c2ecf20Sopenharmony_ciint kfd_reserved_mem_mmap(struct kfd_dev *dev, struct kfd_process *process,
16868c2ecf20Sopenharmony_ci			  struct vm_area_struct *vma)
16878c2ecf20Sopenharmony_ci{
16888c2ecf20Sopenharmony_ci	struct kfd_process_device *pdd;
16898c2ecf20Sopenharmony_ci	struct qcm_process_device *qpd;
16908c2ecf20Sopenharmony_ci
16918c2ecf20Sopenharmony_ci	if ((vma->vm_end - vma->vm_start) != KFD_CWSR_TBA_TMA_SIZE) {
16928c2ecf20Sopenharmony_ci		pr_err("Incorrect CWSR mapping size.\n");
16938c2ecf20Sopenharmony_ci		return -EINVAL;
16948c2ecf20Sopenharmony_ci	}
16958c2ecf20Sopenharmony_ci
16968c2ecf20Sopenharmony_ci	pdd = kfd_get_process_device_data(dev, process);
16978c2ecf20Sopenharmony_ci	if (!pdd)
16988c2ecf20Sopenharmony_ci		return -EINVAL;
16998c2ecf20Sopenharmony_ci	qpd = &pdd->qpd;
17008c2ecf20Sopenharmony_ci
17018c2ecf20Sopenharmony_ci	qpd->cwsr_kaddr = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO,
17028c2ecf20Sopenharmony_ci					get_order(KFD_CWSR_TBA_TMA_SIZE));
17038c2ecf20Sopenharmony_ci	if (!qpd->cwsr_kaddr) {
17048c2ecf20Sopenharmony_ci		pr_err("Error allocating per process CWSR buffer.\n");
17058c2ecf20Sopenharmony_ci		return -ENOMEM;
17068c2ecf20Sopenharmony_ci	}
17078c2ecf20Sopenharmony_ci
17088c2ecf20Sopenharmony_ci	vma->vm_flags |= VM_IO | VM_DONTCOPY | VM_DONTEXPAND
17098c2ecf20Sopenharmony_ci		| VM_NORESERVE | VM_DONTDUMP | VM_PFNMAP;
17108c2ecf20Sopenharmony_ci	/* Mapping pages to user process */
17118c2ecf20Sopenharmony_ci	return remap_pfn_range(vma, vma->vm_start,
17128c2ecf20Sopenharmony_ci			       PFN_DOWN(__pa(qpd->cwsr_kaddr)),
17138c2ecf20Sopenharmony_ci			       KFD_CWSR_TBA_TMA_SIZE, vma->vm_page_prot);
17148c2ecf20Sopenharmony_ci}
17158c2ecf20Sopenharmony_ci
17168c2ecf20Sopenharmony_civoid kfd_flush_tlb(struct kfd_process_device *pdd)
17178c2ecf20Sopenharmony_ci{
17188c2ecf20Sopenharmony_ci	struct kfd_dev *dev = pdd->dev;
17198c2ecf20Sopenharmony_ci
17208c2ecf20Sopenharmony_ci	if (dev->dqm->sched_policy == KFD_SCHED_POLICY_NO_HWS) {
17218c2ecf20Sopenharmony_ci		/* Nothing to flush until a VMID is assigned, which
17228c2ecf20Sopenharmony_ci		 * only happens when the first queue is created.
17238c2ecf20Sopenharmony_ci		 */
17248c2ecf20Sopenharmony_ci		if (pdd->qpd.vmid)
17258c2ecf20Sopenharmony_ci			amdgpu_amdkfd_flush_gpu_tlb_vmid(dev->kgd,
17268c2ecf20Sopenharmony_ci							pdd->qpd.vmid);
17278c2ecf20Sopenharmony_ci	} else {
17288c2ecf20Sopenharmony_ci		amdgpu_amdkfd_flush_gpu_tlb_pasid(dev->kgd,
17298c2ecf20Sopenharmony_ci						pdd->process->pasid);
17308c2ecf20Sopenharmony_ci	}
17318c2ecf20Sopenharmony_ci}
17328c2ecf20Sopenharmony_ci
17338c2ecf20Sopenharmony_ci#if defined(CONFIG_DEBUG_FS)
17348c2ecf20Sopenharmony_ci
17358c2ecf20Sopenharmony_ciint kfd_debugfs_mqds_by_process(struct seq_file *m, void *data)
17368c2ecf20Sopenharmony_ci{
17378c2ecf20Sopenharmony_ci	struct kfd_process *p;
17388c2ecf20Sopenharmony_ci	unsigned int temp;
17398c2ecf20Sopenharmony_ci	int r = 0;
17408c2ecf20Sopenharmony_ci
17418c2ecf20Sopenharmony_ci	int idx = srcu_read_lock(&kfd_processes_srcu);
17428c2ecf20Sopenharmony_ci
17438c2ecf20Sopenharmony_ci	hash_for_each_rcu(kfd_processes_table, temp, p, kfd_processes) {
17448c2ecf20Sopenharmony_ci		seq_printf(m, "Process %d PASID 0x%x:\n",
17458c2ecf20Sopenharmony_ci			   p->lead_thread->tgid, p->pasid);
17468c2ecf20Sopenharmony_ci
17478c2ecf20Sopenharmony_ci		mutex_lock(&p->mutex);
17488c2ecf20Sopenharmony_ci		r = pqm_debugfs_mqds(m, &p->pqm);
17498c2ecf20Sopenharmony_ci		mutex_unlock(&p->mutex);
17508c2ecf20Sopenharmony_ci
17518c2ecf20Sopenharmony_ci		if (r)
17528c2ecf20Sopenharmony_ci			break;
17538c2ecf20Sopenharmony_ci	}
17548c2ecf20Sopenharmony_ci
17558c2ecf20Sopenharmony_ci	srcu_read_unlock(&kfd_processes_srcu, idx);
17568c2ecf20Sopenharmony_ci
17578c2ecf20Sopenharmony_ci	return r;
17588c2ecf20Sopenharmony_ci}
17598c2ecf20Sopenharmony_ci
17608c2ecf20Sopenharmony_ci#endif
17618c2ecf20Sopenharmony_ci
1762