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