1d87f36a0SRajneesh Bhardwaj // SPDX-License-Identifier: GPL-2.0 OR MIT
245102048SBen Goz /*
3d87f36a0SRajneesh Bhardwaj  * Copyright 2014-2022 Advanced Micro Devices, Inc.
445102048SBen Goz  *
545102048SBen Goz  * Permission is hereby granted, free of charge, to any person obtaining a
645102048SBen Goz  * copy of this software and associated documentation files (the "Software"),
745102048SBen Goz  * to deal in the Software without restriction, including without limitation
845102048SBen Goz  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
945102048SBen Goz  * and/or sell copies of the Software, and to permit persons to whom the
1045102048SBen Goz  * Software is furnished to do so, subject to the following conditions:
1145102048SBen Goz  *
1245102048SBen Goz  * The above copyright notice and this permission notice shall be included in
1345102048SBen Goz  * all copies or substantial portions of the Software.
1445102048SBen Goz  *
1545102048SBen Goz  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1645102048SBen Goz  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1745102048SBen Goz  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
1845102048SBen Goz  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
1945102048SBen Goz  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
2045102048SBen Goz  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
2145102048SBen Goz  * OTHER DEALINGS IN THE SOFTWARE.
2245102048SBen Goz  *
2345102048SBen Goz  */
2445102048SBen Goz 
2545102048SBen Goz #include <linux/slab.h>
2645102048SBen Goz #include <linux/list.h>
2745102048SBen Goz #include "kfd_device_queue_manager.h"
2845102048SBen Goz #include "kfd_priv.h"
2945102048SBen Goz #include "kfd_kernel_queue.h"
30eb82da1dSOak Zeng #include "amdgpu_amdkfd.h"
31d225960cSYunxiang Li #include "amdgpu_reset.h"
3245102048SBen Goz 
get_queue_by_qid(struct process_queue_manager * pqm,unsigned int qid)3345102048SBen Goz static inline struct process_queue_node *get_queue_by_qid(
3445102048SBen Goz 			struct process_queue_manager *pqm, unsigned int qid)
3545102048SBen Goz {
3645102048SBen Goz 	struct process_queue_node *pqn;
3745102048SBen Goz 
3845102048SBen Goz 	list_for_each_entry(pqn, &pqm->queues, process_queue_list) {
39ab7c1648SKent Russell 		if ((pqn->q && pqn->q->properties.queue_id == qid) ||
40ab7c1648SKent Russell 		    (pqn->kq && pqn->kq->queue->properties.queue_id == qid))
4145102048SBen Goz 			return pqn;
4245102048SBen Goz 	}
4345102048SBen Goz 
4445102048SBen Goz 	return NULL;
4545102048SBen Goz }
4645102048SBen Goz 
assign_queue_slot_by_qid(struct process_queue_manager * pqm,unsigned int qid)478668dfc3SDavid Yat Sin static int assign_queue_slot_by_qid(struct process_queue_manager *pqm,
488668dfc3SDavid Yat Sin 				    unsigned int qid)
498668dfc3SDavid Yat Sin {
508668dfc3SDavid Yat Sin 	if (qid >= KFD_MAX_NUM_OF_QUEUES_PER_PROCESS)
518668dfc3SDavid Yat Sin 		return -EINVAL;
528668dfc3SDavid Yat Sin 
538668dfc3SDavid Yat Sin 	if (__test_and_set_bit(qid, pqm->queue_slot_bitmap)) {
548668dfc3SDavid Yat Sin 		pr_err("Cannot create new queue because requested qid(%u) is in use\n", qid);
558668dfc3SDavid Yat Sin 		return -ENOSPC;
568668dfc3SDavid Yat Sin 	}
578668dfc3SDavid Yat Sin 
588668dfc3SDavid Yat Sin 	return 0;
598668dfc3SDavid Yat Sin }
608668dfc3SDavid Yat Sin 
find_available_queue_slot(struct process_queue_manager * pqm,unsigned int * qid)6145102048SBen Goz static int find_available_queue_slot(struct process_queue_manager *pqm,
6245102048SBen Goz 					unsigned int *qid)
6345102048SBen Goz {
6445102048SBen Goz 	unsigned long found;
6545102048SBen Goz 
6645102048SBen Goz 	found = find_first_zero_bit(pqm->queue_slot_bitmap,
67b8cbab04SOded Gabbay 			KFD_MAX_NUM_OF_QUEUES_PER_PROCESS);
6845102048SBen Goz 
6979775b62SKent Russell 	pr_debug("The new slot id %lu\n", found);
7045102048SBen Goz 
71b8cbab04SOded Gabbay 	if (found >= KFD_MAX_NUM_OF_QUEUES_PER_PROCESS) {
728544374cSXiaogang Chen 		pr_info("Cannot open more queues for process with pid %d\n",
738544374cSXiaogang Chen 			pqm->process->lead_thread->pid);
7445102048SBen Goz 		return -ENOMEM;
7545102048SBen Goz 	}
7645102048SBen Goz 
7745102048SBen Goz 	set_bit(found, pqm->queue_slot_bitmap);
7845102048SBen Goz 	*qid = found;
7945102048SBen Goz 
8045102048SBen Goz 	return 0;
8145102048SBen Goz }
8245102048SBen Goz 
kfd_process_dequeue_from_device(struct kfd_process_device * pdd)839fd3f1bfSFelix Kuehling void kfd_process_dequeue_from_device(struct kfd_process_device *pdd)
849fd3f1bfSFelix Kuehling {
858dc1db31SMukul Joshi 	struct kfd_node *dev = pdd->dev;
869fd3f1bfSFelix Kuehling 
879fd3f1bfSFelix Kuehling 	if (pdd->already_dequeued)
889fd3f1bfSFelix Kuehling 		return;
899078a5bfSPrike Liang 	/* The MES context flush needs to filter out the case which the
909078a5bfSPrike Liang 	 * KFD process is created without setting up the MES context and
919078a5bfSPrike Liang 	 * queue for creating a compute queue.
929078a5bfSPrike Liang 	 */
939fd3f1bfSFelix Kuehling 	dev->dqm->ops.process_termination(dev->dqm, &pdd->qpd);
949078a5bfSPrike Liang 	if (dev->kfd->shared_resources.enable_mes && !!pdd->proc_ctx_gpu_addr &&
95d225960cSYunxiang Li 	    down_read_trylock(&dev->adev->reset_domain->sem)) {
96d225960cSYunxiang Li 		amdgpu_mes_flush_shader_debugger(dev->adev,
97d225960cSYunxiang Li 						 pdd->proc_ctx_gpu_addr);
98d225960cSYunxiang Li 		up_read(&dev->adev->reset_domain->sem);
99d225960cSYunxiang Li 	}
1009fd3f1bfSFelix Kuehling 	pdd->already_dequeued = true;
1019fd3f1bfSFelix Kuehling }
1029fd3f1bfSFelix Kuehling 
pqm_set_gws(struct process_queue_manager * pqm,unsigned int qid,void * gws)103eb82da1dSOak Zeng int pqm_set_gws(struct process_queue_manager *pqm, unsigned int qid,
104eb82da1dSOak Zeng 			void *gws)
105eb82da1dSOak Zeng {
106e3de58f8SRajneesh Bhardwaj 	struct mqd_update_info minfo = {0};
1078dc1db31SMukul Joshi 	struct kfd_node *dev = NULL;
108eb82da1dSOak Zeng 	struct process_queue_node *pqn;
109eb82da1dSOak Zeng 	struct kfd_process_device *pdd;
110eb82da1dSOak Zeng 	struct kgd_mem *mem = NULL;
111eb82da1dSOak Zeng 	int ret;
112eb82da1dSOak Zeng 
113eb82da1dSOak Zeng 	pqn = get_queue_by_qid(pqm, qid);
114eb82da1dSOak Zeng 	if (!pqn) {
115eb82da1dSOak Zeng 		pr_err("Queue id does not match any known queue\n");
116eb82da1dSOak Zeng 		return -EINVAL;
117eb82da1dSOak Zeng 	}
118eb82da1dSOak Zeng 
119eb82da1dSOak Zeng 	if (pqn->q)
120eb82da1dSOak Zeng 		dev = pqn->q->device;
121eb82da1dSOak Zeng 	if (WARN_ON(!dev))
122eb82da1dSOak Zeng 		return -ENODEV;
123eb82da1dSOak Zeng 
124eb82da1dSOak Zeng 	pdd = kfd_get_process_device_data(dev, pqm->process);
125eb82da1dSOak Zeng 	if (!pdd) {
126eb82da1dSOak Zeng 		pr_err("Process device data doesn't exist\n");
127eb82da1dSOak Zeng 		return -EINVAL;
128eb82da1dSOak Zeng 	}
129eb82da1dSOak Zeng 
130eb82da1dSOak Zeng 	/* Only allow one queue per process can have GWS assigned */
131eb82da1dSOak Zeng 	if (gws && pdd->qpd.num_gws)
132443e902eSOak Zeng 		return -EBUSY;
133eb82da1dSOak Zeng 
134eb82da1dSOak Zeng 	if (!gws && pdd->qpd.num_gws == 0)
135eb82da1dSOak Zeng 		return -EINVAL;
136eb82da1dSOak Zeng 
13771985559SAlex Sierra 	if ((KFD_GC_VERSION(dev) != IP_VERSION(9, 4, 3) &&
1385f571c61SHawking Zhang 	     KFD_GC_VERSION(dev) != IP_VERSION(9, 4, 4) &&
13971985559SAlex Sierra 	     KFD_GC_VERSION(dev) != IP_VERSION(9, 5, 0)) &&
1405f571c61SHawking Zhang 	    !dev->kfd->shared_resources.enable_mes) {
141eb82da1dSOak Zeng 		if (gws)
142eb82da1dSOak Zeng 			ret = amdgpu_amdkfd_add_gws_to_process(pdd->process->kgd_process_info,
143eb82da1dSOak Zeng 				gws, &mem);
144eb82da1dSOak Zeng 		else
145eb82da1dSOak Zeng 			ret = amdgpu_amdkfd_remove_gws_from_process(pdd->process->kgd_process_info,
146eb82da1dSOak Zeng 				pqn->q->gws);
147eb82da1dSOak Zeng 		if (unlikely(ret))
148eb82da1dSOak Zeng 			return ret;
149eb82da1dSOak Zeng 		pqn->q->gws = mem;
150fc133accSMukul Joshi 	} else {
151fc133accSMukul Joshi 		/*
152fc133accSMukul Joshi 		 * Intentionally set GWS to a non-NULL value
1537a1c5c67SJonathan Kim 		 * for devices that do not use GWS for global wave
1547a1c5c67SJonathan Kim 		 * synchronization but require the formality
1557a1c5c67SJonathan Kim 		 * of setting GWS for cooperative groups.
156fc133accSMukul Joshi 		 */
157fc133accSMukul Joshi 		pqn->q->gws = gws ? ERR_PTR(-ENOMEM) : NULL;
158fc133accSMukul Joshi 	}
159fc133accSMukul Joshi 
16002274fc0SGraham Sider 	pdd->qpd.num_gws = gws ? dev->adev->gds.gws_size : 0;
161e3de58f8SRajneesh Bhardwaj 	minfo.update_flag = gws ? UPDATE_FLAG_IS_GWS : 0;
162eb82da1dSOak Zeng 
163eb82da1dSOak Zeng 	return pqn->q->device->dqm->ops.update_queue(pqn->q->device->dqm,
164e3de58f8SRajneesh Bhardwaj 							pqn->q, &minfo);
165eb82da1dSOak Zeng }
166eb82da1dSOak Zeng 
kfd_process_dequeue_from_all_devices(struct kfd_process * p)1679fd3f1bfSFelix Kuehling void kfd_process_dequeue_from_all_devices(struct kfd_process *p)
1689fd3f1bfSFelix Kuehling {
1696ae27841SAlex Sierra 	int i;
1709fd3f1bfSFelix Kuehling 
1716ae27841SAlex Sierra 	for (i = 0; i < p->n_pdds; i++)
1726ae27841SAlex Sierra 		kfd_process_dequeue_from_device(p->pdds[i]);
1739fd3f1bfSFelix Kuehling }
1749fd3f1bfSFelix Kuehling 
pqm_init(struct process_queue_manager * pqm,struct kfd_process * p)17545102048SBen Goz int pqm_init(struct process_queue_manager *pqm, struct kfd_process *p)
17645102048SBen Goz {
17745102048SBen Goz 	INIT_LIST_HEAD(&pqm->queues);
178b9dd6fbdSChristophe JAILLET 	pqm->queue_slot_bitmap = bitmap_zalloc(KFD_MAX_NUM_OF_QUEUES_PER_PROCESS,
179b9dd6fbdSChristophe JAILLET 					       GFP_KERNEL);
1804eacc26bSKent Russell 	if (!pqm->queue_slot_bitmap)
18145102048SBen Goz 		return -ENOMEM;
18245102048SBen Goz 	pqm->process = p;
18345102048SBen Goz 
18445102048SBen Goz 	return 0;
18545102048SBen Goz }
18645102048SBen Goz 
pqm_clean_queue_resource(struct process_queue_manager * pqm,struct process_queue_node * pqn)187d581ceabSZhenGuo Yin static void pqm_clean_queue_resource(struct process_queue_manager *pqm,
188d581ceabSZhenGuo Yin 				     struct process_queue_node *pqn)
189d581ceabSZhenGuo Yin {
190d581ceabSZhenGuo Yin 	struct kfd_node *dev;
191d581ceabSZhenGuo Yin 	struct kfd_process_device *pdd;
192d581ceabSZhenGuo Yin 
193d581ceabSZhenGuo Yin 	dev = pqn->q->device;
194d581ceabSZhenGuo Yin 
195d581ceabSZhenGuo Yin 	pdd = kfd_get_process_device_data(dev, pqm->process);
196d581ceabSZhenGuo Yin 	if (!pdd) {
197d581ceabSZhenGuo Yin 		pr_err("Process device data doesn't exist\n");
198d581ceabSZhenGuo Yin 		return;
199d581ceabSZhenGuo Yin 	}
200d581ceabSZhenGuo Yin 
201d581ceabSZhenGuo Yin 	if (pqn->q->gws) {
202d581ceabSZhenGuo Yin 		if (KFD_GC_VERSION(pqn->q->device) != IP_VERSION(9, 4, 3) &&
2035f571c61SHawking Zhang 		    KFD_GC_VERSION(pqn->q->device) != IP_VERSION(9, 4, 4) &&
20471985559SAlex Sierra 		    KFD_GC_VERSION(pqn->q->device) != IP_VERSION(9, 5, 0) &&
205d581ceabSZhenGuo Yin 		    !dev->kfd->shared_resources.enable_mes)
206d581ceabSZhenGuo Yin 			amdgpu_amdkfd_remove_gws_from_process(
207d581ceabSZhenGuo Yin 				pqm->process->kgd_process_info, pqn->q->gws);
208d581ceabSZhenGuo Yin 		pdd->qpd.num_gws = 0;
209d581ceabSZhenGuo Yin 	}
210d581ceabSZhenGuo Yin 
211d581ceabSZhenGuo Yin 	if (dev->kfd->shared_resources.enable_mes) {
212c86ad391SPhilip Yang 		amdgpu_amdkfd_free_gtt_mem(dev->adev, &pqn->q->gang_ctx_bo);
213fb910658SPhilip Yang 		amdgpu_amdkfd_free_gtt_mem(dev->adev, (void **)&pqn->q->wptr_bo_gart);
214d581ceabSZhenGuo Yin 	}
215d581ceabSZhenGuo Yin }
216d581ceabSZhenGuo Yin 
pqm_uninit(struct process_queue_manager * pqm)21745102048SBen Goz void pqm_uninit(struct process_queue_manager *pqm)
21845102048SBen Goz {
21945102048SBen Goz 	struct process_queue_node *pqn, *next;
22045102048SBen Goz 
22145102048SBen Goz 	list_for_each_entry_safe(pqn, next, &pqm->queues, process_queue_list) {
222fb910658SPhilip Yang 		if (pqn->q) {
223a592bb19SAndrew Martin 			struct kfd_process_device *pdd = kfd_get_process_device_data(pqn->q->device,
224a592bb19SAndrew Martin 										     pqm->process);
225a592bb19SAndrew Martin 			if (pdd) {
226a1fc9f58SPhilip Yang 				kfd_queue_unref_bo_vas(pdd, &pqn->q->properties);
227fb910658SPhilip Yang 				kfd_queue_release_buffers(pdd, &pqn->q->properties);
228a592bb19SAndrew Martin 			} else {
229a592bb19SAndrew Martin 				WARN_ON(!pdd);
230a592bb19SAndrew Martin 			}
231d581ceabSZhenGuo Yin 			pqm_clean_queue_resource(pqm, pqn);
232fb910658SPhilip Yang 		}
233d581ceabSZhenGuo Yin 
234dcdb4d90SPhilip Yang 		kfd_procfs_del_queue(pqn->q);
2359fd3f1bfSFelix Kuehling 		uninit_queue(pqn->q);
2369fd3f1bfSFelix Kuehling 		list_del(&pqn->process_queue_list);
2379fd3f1bfSFelix Kuehling 		kfree(pqn);
2389fd3f1bfSFelix Kuehling 	}
23945102048SBen Goz 
240b9dd6fbdSChristophe JAILLET 	bitmap_free(pqm->queue_slot_bitmap);
24145102048SBen Goz 	pqm->queue_slot_bitmap = NULL;
24245102048SBen Goz }
24345102048SBen Goz 
init_user_queue(struct process_queue_manager * pqm,struct kfd_node * dev,struct queue ** q,struct queue_properties * q_properties,unsigned int qid)2442d030d3eSYong Zhao static int init_user_queue(struct process_queue_manager *pqm,
2458dc1db31SMukul Joshi 				struct kfd_node *dev, struct queue **q,
24645102048SBen Goz 				struct queue_properties *q_properties,
247bc4688aeSLang Yu 				unsigned int qid)
24845102048SBen Goz {
24945102048SBen Goz 	int retval;
25045102048SBen Goz 
25145102048SBen Goz 	/* Doorbell initialized in user space*/
25245102048SBen Goz 	q_properties->doorbell_ptr = NULL;
253a70a93faSJonathan Kim 	q_properties->exception_status = KFD_EC_MASK(EC_QUEUE_NEW);
25445102048SBen Goz 
25545102048SBen Goz 	/* let DQM handle it*/
25645102048SBen Goz 	q_properties->vmid = 0;
25745102048SBen Goz 	q_properties->queue_id = qid;
25845102048SBen Goz 
259e88a614cSEdward O'Callaghan 	retval = init_queue(q, q_properties);
26045102048SBen Goz 	if (retval != 0)
261ab7c1648SKent Russell 		return retval;
26245102048SBen Goz 
26345102048SBen Goz 	(*q)->device = dev;
26445102048SBen Goz 	(*q)->process = pqm->process;
26545102048SBen Goz 
2668dc1db31SMukul Joshi 	if (dev->kfd->shared_resources.enable_mes) {
267cc009e61SMukul Joshi 		retval = amdgpu_amdkfd_alloc_gtt_mem(dev->adev,
268cc009e61SMukul Joshi 						AMDGPU_MES_GANG_CTX_SIZE,
269cc009e61SMukul Joshi 						&(*q)->gang_ctx_bo,
270cc009e61SMukul Joshi 						&(*q)->gang_ctx_gpu_addr,
271cc009e61SMukul Joshi 						&(*q)->gang_ctx_cpu_ptr,
272cc009e61SMukul Joshi 						false);
273cc009e61SMukul Joshi 		if (retval) {
274cc009e61SMukul Joshi 			pr_err("failed to allocate gang context bo\n");
275cc009e61SMukul Joshi 			goto cleanup;
276cc009e61SMukul Joshi 		}
277cc009e61SMukul Joshi 		memset((*q)->gang_ctx_cpu_ptr, 0, AMDGPU_MES_GANG_CTX_SIZE);
278fb910658SPhilip Yang 
279fb910658SPhilip Yang 		/* Starting with GFX11, wptr BOs must be mapped to GART for MES to determine work
280fb910658SPhilip Yang 		 * on unmapped queues for usermode queue oversubscription (no aggregated doorbell)
281fb910658SPhilip Yang 		 */
282fb910658SPhilip Yang 		if (((dev->adev->mes.sched_version & AMDGPU_MES_API_VERSION_MASK)
283fb910658SPhilip Yang 		    >> AMDGPU_MES_API_VERSION_SHIFT) >= 2) {
284fb910658SPhilip Yang 			if (dev->adev != amdgpu_ttm_adev(q_properties->wptr_bo->tbo.bdev)) {
285fb910658SPhilip Yang 				pr_err("Queue memory allocated to wrong device\n");
286fb910658SPhilip Yang 				retval = -EINVAL;
287fb910658SPhilip Yang 				goto free_gang_ctx_bo;
288fb910658SPhilip Yang 			}
289fb910658SPhilip Yang 
290fb910658SPhilip Yang 			retval = amdgpu_amdkfd_map_gtt_bo_to_gart(q_properties->wptr_bo,
291fb910658SPhilip Yang 								  &(*q)->wptr_bo_gart);
292fb910658SPhilip Yang 			if (retval) {
293fb910658SPhilip Yang 				pr_err("Failed to map wptr bo to GART\n");
294fb910658SPhilip Yang 				goto free_gang_ctx_bo;
295fb910658SPhilip Yang 			}
296fb910658SPhilip Yang 		}
297cc009e61SMukul Joshi 	}
29845102048SBen Goz 
299cc009e61SMukul Joshi 	pr_debug("PQM After init queue");
300cc009e61SMukul Joshi 	return 0;
301cc009e61SMukul Joshi 
302fb910658SPhilip Yang free_gang_ctx_bo:
303a33f7f96SZhu Lingshan 	amdgpu_amdkfd_free_gtt_mem(dev->adev, &(*q)->gang_ctx_bo);
304cc009e61SMukul Joshi cleanup:
305cc009e61SMukul Joshi 	uninit_queue(*q);
3060b255ab7SChia-I Wu 	*q = NULL;
30745102048SBen Goz 	return retval;
30845102048SBen Goz }
30945102048SBen Goz 
pqm_create_queue(struct process_queue_manager * pqm,struct kfd_node * dev,struct queue_properties * properties,unsigned int * qid,const struct kfd_criu_queue_priv_data * q_data,const void * restore_mqd,const void * restore_ctl_stack,uint32_t * p_doorbell_offset_in_process)31045102048SBen Goz int pqm_create_queue(struct process_queue_manager *pqm,
3118dc1db31SMukul Joshi 			    struct kfd_node *dev,
31245102048SBen Goz 			    struct queue_properties *properties,
313e47a8b52SYong Zhao 			    unsigned int *qid,
3148668dfc3SDavid Yat Sin 			    const struct kfd_criu_queue_priv_data *q_data,
31542c6c482SDavid Yat Sin 			    const void *restore_mqd,
3163a9822d7SDavid Yat Sin 			    const void *restore_ctl_stack,
317e47a8b52SYong Zhao 			    uint32_t *p_doorbell_offset_in_process)
31845102048SBen Goz {
31945102048SBen Goz 	int retval;
32045102048SBen Goz 	struct kfd_process_device *pdd;
32145102048SBen Goz 	struct queue *q;
32245102048SBen Goz 	struct process_queue_node *pqn;
32345102048SBen Goz 	struct kernel_queue *kq;
324e6f791b1SYong Zhao 	enum kfd_queue_type type = properties->type;
32536c2d7ebSFelix Kuehling 	unsigned int max_queues = 127; /* HWS limit */
32645102048SBen Goz 
3273697b9bdSMukul Joshi 	/*
32871985559SAlex Sierra 	 * On GFX 9.4.3/9.5.0, increase the number of queues that
32971985559SAlex Sierra 	 * can be created to 255. No HWS limit on GFX 9.4.3/9.5.0.
3303697b9bdSMukul Joshi 	 */
3315f571c61SHawking Zhang 	if (KFD_GC_VERSION(dev) == IP_VERSION(9, 4, 3) ||
33271985559SAlex Sierra 	    KFD_GC_VERSION(dev) == IP_VERSION(9, 4, 4) ||
33371985559SAlex Sierra 	    KFD_GC_VERSION(dev) == IP_VERSION(9, 5, 0))
3343697b9bdSMukul Joshi 		max_queues = 255;
3353697b9bdSMukul Joshi 
33645102048SBen Goz 	q = NULL;
33745102048SBen Goz 	kq = NULL;
33845102048SBen Goz 
339093c7d8cSAlexey Skidanov 	pdd = kfd_get_process_device_data(dev, pqm->process);
340093c7d8cSAlexey Skidanov 	if (!pdd) {
341093c7d8cSAlexey Skidanov 		pr_err("Process device data doesn't exist\n");
342093c7d8cSAlexey Skidanov 		return -1;
343093c7d8cSAlexey Skidanov 	}
34445102048SBen Goz 
345992839adSYair Shachar 	/*
346992839adSYair Shachar 	 * for debug process, verify that it is within the static queues limit
347992839adSYair Shachar 	 * currently limit is set to half of the total avail HQD slots
348992839adSYair Shachar 	 * If we are just about to create DIQ, the is_debug flag is not set yet
349992839adSYair Shachar 	 * Hence we also check the type as well
350992839adSYair Shachar 	 */
35136c2d7ebSFelix Kuehling 	if ((pdd->qpd.is_debug) || (type == KFD_QUEUE_TYPE_DIQ))
3528dc1db31SMukul Joshi 		max_queues = dev->kfd->device_info.max_no_of_hqd/2;
35336c2d7ebSFelix Kuehling 
35436c2d7ebSFelix Kuehling 	if (pdd->qpd.queue_count >= max_queues)
355ab7c1648SKent Russell 		return -ENOSPC;
356992839adSYair Shachar 
3578668dfc3SDavid Yat Sin 	if (q_data) {
3588668dfc3SDavid Yat Sin 		retval = assign_queue_slot_by_qid(pqm, q_data->q_id);
3598668dfc3SDavid Yat Sin 		*qid = q_data->q_id;
3608668dfc3SDavid Yat Sin 	} else
36145102048SBen Goz 		retval = find_available_queue_slot(pqm, qid);
3628668dfc3SDavid Yat Sin 
36345102048SBen Goz 	if (retval != 0)
36445102048SBen Goz 		return retval;
36545102048SBen Goz 
366fddc4502SSrinivasan Shanmugam 	/* Register process if this is the first queue */
3675a29ad6bSBen Goz 	if (list_empty(&pdd->qpd.queues_list) &&
368b20cd0dfSFelix Kuehling 	    list_empty(&pdd->qpd.priv_queue_list))
36945c9a5e4SOded Gabbay 		dev->dqm->ops.register_process(dev->dqm, &pdd->qpd);
37045102048SBen Goz 
371fddc4502SSrinivasan Shanmugam 	/* Allocate proc_ctx_bo only if MES is enabled and this is the first queue */
372fddc4502SSrinivasan Shanmugam 	if (!pdd->proc_ctx_cpu_ptr && dev->kfd->shared_resources.enable_mes) {
373fddc4502SSrinivasan Shanmugam 		retval = amdgpu_amdkfd_alloc_gtt_mem(dev->adev,
374fddc4502SSrinivasan Shanmugam 						     AMDGPU_MES_PROC_CTX_SIZE,
375fddc4502SSrinivasan Shanmugam 						     &pdd->proc_ctx_bo,
376fddc4502SSrinivasan Shanmugam 						     &pdd->proc_ctx_gpu_addr,
377fddc4502SSrinivasan Shanmugam 						     &pdd->proc_ctx_cpu_ptr,
378fddc4502SSrinivasan Shanmugam 						     false);
379fddc4502SSrinivasan Shanmugam 		if (retval) {
380fddc4502SSrinivasan Shanmugam 			dev_err(dev->adev->dev, "failed to allocate process context bo\n");
381fddc4502SSrinivasan Shanmugam 			return retval;
382fddc4502SSrinivasan Shanmugam 		}
383fddc4502SSrinivasan Shanmugam 		memset(pdd->proc_ctx_cpu_ptr, 0, AMDGPU_MES_PROC_CTX_SIZE);
384fddc4502SSrinivasan Shanmugam 	}
385fddc4502SSrinivasan Shanmugam 
386dbf56ab1SKent Russell 	pqn = kzalloc(sizeof(*pqn), GFP_KERNEL);
38745102048SBen Goz 	if (!pqn) {
38845102048SBen Goz 		retval = -ENOMEM;
38945102048SBen Goz 		goto err_allocate_pqn;
39045102048SBen Goz 	}
39145102048SBen Goz 
39245102048SBen Goz 	switch (type) {
393bcea3081SBen Goz 	case KFD_QUEUE_TYPE_SDMA:
3941b4670f6SOak Zeng 	case KFD_QUEUE_TYPE_SDMA_XGMI:
395e06b71b2SJonathan Kim 	case KFD_QUEUE_TYPE_SDMA_BY_ENG_ID:
396c7637c95SYong Zhao 		/* SDMA queues are always allocated statically no matter
397c7637c95SYong Zhao 		 * which scheduler mode is used. We also do not need to
398c7637c95SYong Zhao 		 * check whether a SDMA queue can be allocated here, because
399c7637c95SYong Zhao 		 * allocate_sdma_queue() in create_queue() has the
400c7637c95SYong Zhao 		 * corresponding check logic.
401c7637c95SYong Zhao 		 */
402bc4688aeSLang Yu 		retval = init_user_queue(pqm, dev, &q, properties, *qid);
4038c946b89SFelix Kuehling 		if (retval != 0)
4048c946b89SFelix Kuehling 			goto err_create_queue;
4058c946b89SFelix Kuehling 		pqn->q = q;
4068c946b89SFelix Kuehling 		pqn->kq = NULL;
4073a9822d7SDavid Yat Sin 		retval = dev->dqm->ops.create_queue(dev->dqm, q, &pdd->qpd, q_data,
4083a9822d7SDavid Yat Sin 						    restore_mqd, restore_ctl_stack);
4098c946b89SFelix Kuehling 		print_queue(q);
4108c946b89SFelix Kuehling 		break;
4118c946b89SFelix Kuehling 
41245102048SBen Goz 	case KFD_QUEUE_TYPE_COMPUTE:
41345102048SBen Goz 		/* check if there is over subscription */
414d146c5a7SFelix Kuehling 		if ((dev->dqm->sched_policy ==
415d146c5a7SFelix Kuehling 		     KFD_SCHED_POLICY_HWS_NO_OVERSUBSCRIPTION) &&
41644008d7aSYong Zhao 		((dev->dqm->processes_count >= dev->vm_info.vmid_num_kfd) ||
417e6945304SYong Zhao 		(dev->dqm->active_queue_count >= get_cp_queues_num(dev->dqm)))) {
418e7016d8eSYong Zhao 			pr_debug("Over-subscription is not allowed when amdkfd.sched_policy == 1\n");
41945102048SBen Goz 			retval = -EPERM;
42045102048SBen Goz 			goto err_create_queue;
42145102048SBen Goz 		}
42245102048SBen Goz 
423bc4688aeSLang Yu 		retval = init_user_queue(pqm, dev, &q, properties, *qid);
42445102048SBen Goz 		if (retval != 0)
42545102048SBen Goz 			goto err_create_queue;
42645102048SBen Goz 		pqn->q = q;
42745102048SBen Goz 		pqn->kq = NULL;
4283a9822d7SDavid Yat Sin 		retval = dev->dqm->ops.create_queue(dev->dqm, q, &pdd->qpd, q_data,
4293a9822d7SDavid Yat Sin 						    restore_mqd, restore_ctl_stack);
43045102048SBen Goz 		print_queue(q);
43145102048SBen Goz 		break;
43245102048SBen Goz 	case KFD_QUEUE_TYPE_DIQ:
43345102048SBen Goz 		kq = kernel_queue_init(dev, KFD_QUEUE_TYPE_DIQ);
4344eacc26bSKent Russell 		if (!kq) {
435e048a0b2SDan Carpenter 			retval = -ENOMEM;
43645102048SBen Goz 			goto err_create_queue;
43745102048SBen Goz 		}
43845102048SBen Goz 		kq->queue->properties.queue_id = *qid;
43945102048SBen Goz 		pqn->kq = kq;
44045102048SBen Goz 		pqn->q = NULL;
44112fb1ad7SJonathan Kim 		retval = kfd_process_drain_interrupts(pdd);
44212fb1ad7SJonathan Kim 		if (retval)
44312fb1ad7SJonathan Kim 			break;
44412fb1ad7SJonathan Kim 
44545c9a5e4SOded Gabbay 		retval = dev->dqm->ops.create_kernel_queue(dev->dqm,
44645c9a5e4SOded Gabbay 							kq, &pdd->qpd);
44745102048SBen Goz 		break;
44845102048SBen Goz 	default:
44932fa8219SFelix Kuehling 		WARN(1, "Invalid queue type %d", type);
45032fa8219SFelix Kuehling 		retval = -EINVAL;
45145102048SBen Goz 	}
45245102048SBen Goz 
45345102048SBen Goz 	if (retval != 0) {
4548544374cSXiaogang Chen 		pr_err("process pid %d DQM create queue type %d failed. ret %d\n",
4558544374cSXiaogang Chen 			pqm->process->lead_thread->pid, type, retval);
45645102048SBen Goz 		goto err_create_queue;
45745102048SBen Goz 	}
45845102048SBen Goz 
4592105a15aSShashank Sharma 	if (q && p_doorbell_offset_in_process) {
460ef568db7SFelix Kuehling 		/* Return the doorbell offset within the doorbell page
461ef568db7SFelix Kuehling 		 * to the caller so it can be passed up to user mode
462ef568db7SFelix Kuehling 		 * (in bytes).
4632105a15aSShashank Sharma 		 * relative doorbell index = Absolute doorbell index -
4642105a15aSShashank Sharma 		 * absolute index of first doorbell in the page.
465ef568db7SFelix Kuehling 		 */
4662105a15aSShashank Sharma 		uint32_t first_db_index = amdgpu_doorbell_index_on_bar(pdd->dev->adev,
4672105a15aSShashank Sharma 								       pdd->qpd.proc_doorbells,
468367a0af4SArvind Yadav 								       0,
469367a0af4SArvind Yadav 								       pdd->dev->kfd->device_info.doorbell_size);
4702105a15aSShashank Sharma 
4712105a15aSShashank Sharma 		*p_doorbell_offset_in_process = (q->properties.doorbell_off
4722105a15aSShashank Sharma 						- first_db_index) * sizeof(uint32_t);
4732105a15aSShashank Sharma 	}
474ef568db7SFelix Kuehling 
47579775b62SKent Russell 	pr_debug("PQM After DQM create queue\n");
47645102048SBen Goz 
47745102048SBen Goz 	list_add(&pqn->process_queue_list, &pqm->queues);
47845102048SBen Goz 
47945102048SBen Goz 	if (q) {
48079775b62SKent Russell 		pr_debug("PQM done creating queue\n");
4816d220a7eSAmber Lin 		kfd_procfs_add_queue(q);
482e6f791b1SYong Zhao 		print_queue_properties(&q->properties);
48345102048SBen Goz 	}
48445102048SBen Goz 
48545102048SBen Goz 	return retval;
48645102048SBen Goz 
48745102048SBen Goz err_create_queue:
48866f28b9aSYong Zhao 	uninit_queue(q);
48966f28b9aSYong Zhao 	if (kq)
4901802b042SYunxiang Li 		kernel_queue_uninit(kq);
49145102048SBen Goz 	kfree(pqn);
49245102048SBen Goz err_allocate_pqn:
493f046bfdfSBen Goz 	/* check if queues list is empty unregister process from device */
49445102048SBen Goz 	clear_bit(*qid, pqm->queue_slot_bitmap);
4955a29ad6bSBen Goz 	if (list_empty(&pdd->qpd.queues_list) &&
4965a29ad6bSBen Goz 	    list_empty(&pdd->qpd.priv_queue_list))
497b3869b17SDave Airlie 		dev->dqm->ops.unregister_process(dev->dqm, &pdd->qpd);
49845102048SBen Goz 	return retval;
49945102048SBen Goz }
50045102048SBen Goz 
pqm_destroy_queue(struct process_queue_manager * pqm,unsigned int qid)50145102048SBen Goz int pqm_destroy_queue(struct process_queue_manager *pqm, unsigned int qid)
50245102048SBen Goz {
50345102048SBen Goz 	struct process_queue_node *pqn;
50445102048SBen Goz 	struct kfd_process_device *pdd;
50545102048SBen Goz 	struct device_queue_manager *dqm;
5068dc1db31SMukul Joshi 	struct kfd_node *dev;
50745102048SBen Goz 	int retval;
50845102048SBen Goz 
50945102048SBen Goz 	dqm = NULL;
51045102048SBen Goz 
51145102048SBen Goz 	retval = 0;
51245102048SBen Goz 
51345102048SBen Goz 	pqn = get_queue_by_qid(pqm, qid);
5144eacc26bSKent Russell 	if (!pqn) {
51579775b62SKent Russell 		pr_err("Queue id does not match any known queue\n");
51645102048SBen Goz 		return -EINVAL;
51745102048SBen Goz 	}
51845102048SBen Goz 
51945102048SBen Goz 	dev = NULL;
52045102048SBen Goz 	if (pqn->kq)
52145102048SBen Goz 		dev = pqn->kq->dev;
52245102048SBen Goz 	if (pqn->q)
52345102048SBen Goz 		dev = pqn->q->device;
52432fa8219SFelix Kuehling 	if (WARN_ON(!dev))
52532fa8219SFelix Kuehling 		return -ENODEV;
52645102048SBen Goz 
527093c7d8cSAlexey Skidanov 	pdd = kfd_get_process_device_data(dev, pqm->process);
528093c7d8cSAlexey Skidanov 	if (!pdd) {
529093c7d8cSAlexey Skidanov 		pr_err("Process device data doesn't exist\n");
530093c7d8cSAlexey Skidanov 		return -1;
531093c7d8cSAlexey Skidanov 	}
53245102048SBen Goz 
53345102048SBen Goz 	if (pqn->kq) {
53445102048SBen Goz 		/* destroy kernel queue (DIQ) */
53545102048SBen Goz 		dqm = pqn->kq->dev->dqm;
53645c9a5e4SOded Gabbay 		dqm->ops.destroy_kernel_queue(dqm, pqn->kq, &pdd->qpd);
5371802b042SYunxiang Li 		kernel_queue_uninit(pqn->kq);
53845102048SBen Goz 	}
53945102048SBen Goz 
54045102048SBen Goz 	if (pqn->q) {
541a1fc9f58SPhilip Yang 		retval = kfd_queue_unref_bo_vas(pdd, &pqn->q->properties);
542fb910658SPhilip Yang 		if (retval)
543fb910658SPhilip Yang 			goto err_destroy_queue;
544fb910658SPhilip Yang 
54545102048SBen Goz 		dqm = pqn->q->device->dqm;
54645c9a5e4SOded Gabbay 		retval = dqm->ops.destroy_queue(dqm, &pdd->qpd, pqn->q);
5473c0b4280SPhilip Yang 		if (retval) {
5486027b1bfSYong Zhao 			pr_err("Pasid 0x%x destroy queue %d failed, ret %d\n",
5498544374cSXiaogang Chen 				pdd->pasid,
5502533f074SShaoyun Liu 				pqn->q->properties.queue_id, retval);
551*7919b4caSPhilip Yang 			if (retval != -ETIME && retval != -EIO)
5523c0b4280SPhilip Yang 				goto err_destroy_queue;
5533c0b4280SPhilip Yang 		}
55421d1d724SKent Russell 		kfd_procfs_del_queue(pqn->q);
555a1fc9f58SPhilip Yang 		kfd_queue_release_buffers(pdd, &pqn->q->properties);
556d581ceabSZhenGuo Yin 		pqm_clean_queue_resource(pqm, pqn);
55745102048SBen Goz 		uninit_queue(pqn->q);
55845102048SBen Goz 	}
55945102048SBen Goz 
56045102048SBen Goz 	list_del(&pqn->process_queue_list);
56145102048SBen Goz 	kfree(pqn);
56245102048SBen Goz 	clear_bit(qid, pqm->queue_slot_bitmap);
56345102048SBen Goz 
5645a29ad6bSBen Goz 	if (list_empty(&pdd->qpd.queues_list) &&
5655a29ad6bSBen Goz 	    list_empty(&pdd->qpd.priv_queue_list))
56645c9a5e4SOded Gabbay 		dqm->ops.unregister_process(dqm, &pdd->qpd);
56745102048SBen Goz 
5683c0b4280SPhilip Yang err_destroy_queue:
56945102048SBen Goz 	return retval;
57045102048SBen Goz }
57145102048SBen Goz 
pqm_update_queue_properties(struct process_queue_manager * pqm,unsigned int qid,struct queue_properties * p)5727c695a2cSLang Yu int pqm_update_queue_properties(struct process_queue_manager *pqm,
5737c695a2cSLang Yu 				unsigned int qid, struct queue_properties *p)
57445102048SBen Goz {
57545102048SBen Goz 	int retval;
57645102048SBen Goz 	struct process_queue_node *pqn;
57745102048SBen Goz 
57845102048SBen Goz 	pqn = get_queue_by_qid(pqm, qid);
579305cd109SPhilip Yang 	if (!pqn || !pqn->q) {
58079775b62SKent Russell 		pr_debug("No queue %d exists for update operation\n", qid);
581b9dce23dSOded Gabbay 		return -EFAULT;
582b9dce23dSOded Gabbay 	}
58345102048SBen Goz 
584305cd109SPhilip Yang 	/*
585305cd109SPhilip Yang 	 * Update with NULL ring address is used to disable the queue
586305cd109SPhilip Yang 	 */
587305cd109SPhilip Yang 	if (p->queue_address && p->queue_size) {
588305cd109SPhilip Yang 		struct kfd_process_device *pdd;
589305cd109SPhilip Yang 		struct amdgpu_vm *vm;
590305cd109SPhilip Yang 		struct queue *q = pqn->q;
591305cd109SPhilip Yang 		int err;
592305cd109SPhilip Yang 
593305cd109SPhilip Yang 		pdd = kfd_get_process_device_data(q->device, q->process);
594305cd109SPhilip Yang 		if (!pdd)
595305cd109SPhilip Yang 			return -ENODEV;
596305cd109SPhilip Yang 		vm = drm_priv_to_vm(pdd->drm_priv);
597305cd109SPhilip Yang 		err = amdgpu_bo_reserve(vm->root.bo, false);
598305cd109SPhilip Yang 		if (err)
599305cd109SPhilip Yang 			return err;
600305cd109SPhilip Yang 
601305cd109SPhilip Yang 		if (kfd_queue_buffer_get(vm, (void *)p->queue_address, &p->ring_bo,
602305cd109SPhilip Yang 					 p->queue_size)) {
603305cd109SPhilip Yang 			pr_debug("ring buf 0x%llx size 0x%llx not mapped on GPU\n",
604305cd109SPhilip Yang 				 p->queue_address, p->queue_size);
605305cd109SPhilip Yang 			return -EFAULT;
606305cd109SPhilip Yang 		}
607305cd109SPhilip Yang 
608a1fc9f58SPhilip Yang 		kfd_queue_unref_bo_va(vm, &pqn->q->properties.ring_bo);
609a1fc9f58SPhilip Yang 		kfd_queue_buffer_put(&pqn->q->properties.ring_bo);
610305cd109SPhilip Yang 		amdgpu_bo_unreserve(vm->root.bo);
611305cd109SPhilip Yang 
612305cd109SPhilip Yang 		pqn->q->properties.ring_bo = p->ring_bo;
613305cd109SPhilip Yang 	}
614305cd109SPhilip Yang 
61545102048SBen Goz 	pqn->q->properties.queue_address = p->queue_address;
61645102048SBen Goz 	pqn->q->properties.queue_size = p->queue_size;
61745102048SBen Goz 	pqn->q->properties.queue_percent = p->queue_percent;
61845102048SBen Goz 	pqn->q->properties.priority = p->priority;
6193c8bdb51SMukul Joshi 	pqn->q->properties.pm4_target_xcc = p->pm4_target_xcc;
62045102048SBen Goz 
62145c9a5e4SOded Gabbay 	retval = pqn->q->device->dqm->ops.update_queue(pqn->q->device->dqm,
622c6e559ebSLang Yu 							pqn->q, NULL);
62345102048SBen Goz 	if (retval != 0)
62445102048SBen Goz 		return retval;
62545102048SBen Goz 
62645102048SBen Goz 	return 0;
62745102048SBen Goz }
62845102048SBen Goz 
pqm_update_mqd(struct process_queue_manager * pqm,unsigned int qid,struct mqd_update_info * minfo)6297c695a2cSLang Yu int pqm_update_mqd(struct process_queue_manager *pqm,
6307c695a2cSLang Yu 				unsigned int qid, struct mqd_update_info *minfo)
63139e7f331SFelix Kuehling {
63239e7f331SFelix Kuehling 	int retval;
63339e7f331SFelix Kuehling 	struct process_queue_node *pqn;
63439e7f331SFelix Kuehling 
63539e7f331SFelix Kuehling 	pqn = get_queue_by_qid(pqm, qid);
63639e7f331SFelix Kuehling 	if (!pqn) {
63739e7f331SFelix Kuehling 		pr_debug("No queue %d exists for update operation\n", qid);
63839e7f331SFelix Kuehling 		return -EFAULT;
63939e7f331SFelix Kuehling 	}
64039e7f331SFelix Kuehling 
64169a8c3aeSJonathan Kim 	/* CUs are masked for debugger requirements so deny user mask  */
64269a8c3aeSJonathan Kim 	if (pqn->q->properties.is_dbg_wa && minfo && minfo->cu_mask.ptr)
64369a8c3aeSJonathan Kim 		return -EBUSY;
64469a8c3aeSJonathan Kim 
645cff35798SJonathan Kim 	/* ASICs that have WGPs must enforce pairwise enabled mask checks. */
64669a8c3aeSJonathan Kim 	if (minfo && minfo->cu_mask.ptr &&
647cff35798SJonathan Kim 			KFD_GC_VERSION(pqn->q->device) >= IP_VERSION(10, 0, 0)) {
648cff35798SJonathan Kim 		int i;
649cff35798SJonathan Kim 
650cff35798SJonathan Kim 		for (i = 0; i < minfo->cu_mask.count; i += 2) {
651cff35798SJonathan Kim 			uint32_t cu_pair = (minfo->cu_mask.ptr[i / 32] >> (i % 32)) & 0x3;
652cff35798SJonathan Kim 
653cff35798SJonathan Kim 			if (cu_pair && cu_pair != 0x3) {
654cff35798SJonathan Kim 				pr_debug("CUs must be adjacent pairwise enabled.\n");
655cff35798SJonathan Kim 				return -EINVAL;
656cff35798SJonathan Kim 			}
657cff35798SJonathan Kim 		}
658cff35798SJonathan Kim 	}
659cff35798SJonathan Kim 
66039e7f331SFelix Kuehling 	retval = pqn->q->device->dqm->ops.update_queue(pqn->q->device->dqm,
6617c695a2cSLang Yu 							pqn->q, minfo);
66239e7f331SFelix Kuehling 	if (retval != 0)
66339e7f331SFelix Kuehling 		return retval;
66439e7f331SFelix Kuehling 
66569a8c3aeSJonathan Kim 	if (minfo && minfo->cu_mask.ptr)
66669a8c3aeSJonathan Kim 		pqn->q->properties.is_user_cu_masked = true;
66769a8c3aeSJonathan Kim 
66839e7f331SFelix Kuehling 	return 0;
66939e7f331SFelix Kuehling }
67039e7f331SFelix Kuehling 
pqm_get_user_queue(struct process_queue_manager * pqm,unsigned int qid)6715bb4b78bSOak Zeng struct queue *pqm_get_user_queue(struct process_queue_manager *pqm,
6725bb4b78bSOak Zeng 					unsigned int qid)
6735bb4b78bSOak Zeng {
6745bb4b78bSOak Zeng 	struct process_queue_node *pqn;
6755bb4b78bSOak Zeng 
6765bb4b78bSOak Zeng 	pqn = get_queue_by_qid(pqm, qid);
6775bb4b78bSOak Zeng 	return pqn ? pqn->q : NULL;
6785bb4b78bSOak Zeng }
6795bb4b78bSOak Zeng 
pqm_get_wave_state(struct process_queue_manager * pqm,unsigned int qid,void __user * ctl_stack,u32 * ctl_stack_used_size,u32 * save_area_used_size)6805df099e8SJay Cornwall int pqm_get_wave_state(struct process_queue_manager *pqm,
6815df099e8SJay Cornwall 		       unsigned int qid,
6825df099e8SJay Cornwall 		       void __user *ctl_stack,
6835df099e8SJay Cornwall 		       u32 *ctl_stack_used_size,
6845df099e8SJay Cornwall 		       u32 *save_area_used_size)
6855df099e8SJay Cornwall {
6865df099e8SJay Cornwall 	struct process_queue_node *pqn;
6875df099e8SJay Cornwall 
6885df099e8SJay Cornwall 	pqn = get_queue_by_qid(pqm, qid);
6895df099e8SJay Cornwall 	if (!pqn) {
6905df099e8SJay Cornwall 		pr_debug("amdkfd: No queue %d exists for operation\n",
6915df099e8SJay Cornwall 			 qid);
6925df099e8SJay Cornwall 		return -EFAULT;
6935df099e8SJay Cornwall 	}
6945df099e8SJay Cornwall 
6955df099e8SJay Cornwall 	return pqn->q->device->dqm->ops.get_wave_state(pqn->q->device->dqm,
6965df099e8SJay Cornwall 						       pqn->q,
6975df099e8SJay Cornwall 						       ctl_stack,
6985df099e8SJay Cornwall 						       ctl_stack_used_size,
6995df099e8SJay Cornwall 						       save_area_used_size);
7005df099e8SJay Cornwall }
7015df099e8SJay Cornwall 
pqm_get_queue_snapshot(struct process_queue_manager * pqm,uint64_t exception_clear_mask,void __user * buf,int * num_qss_entries,uint32_t * entry_size)702b17bd5dbSJonathan Kim int pqm_get_queue_snapshot(struct process_queue_manager *pqm,
703b17bd5dbSJonathan Kim 			   uint64_t exception_clear_mask,
704b17bd5dbSJonathan Kim 			   void __user *buf,
705b17bd5dbSJonathan Kim 			   int *num_qss_entries,
706b17bd5dbSJonathan Kim 			   uint32_t *entry_size)
707b17bd5dbSJonathan Kim {
708b17bd5dbSJonathan Kim 	struct process_queue_node *pqn;
709b17bd5dbSJonathan Kim 	struct kfd_queue_snapshot_entry src;
710b17bd5dbSJonathan Kim 	uint32_t tmp_entry_size = *entry_size, tmp_qss_entries = *num_qss_entries;
711b17bd5dbSJonathan Kim 	int r = 0;
712b17bd5dbSJonathan Kim 
713b17bd5dbSJonathan Kim 	*num_qss_entries = 0;
714b17bd5dbSJonathan Kim 	if (!(*entry_size))
715b17bd5dbSJonathan Kim 		return -EINVAL;
716b17bd5dbSJonathan Kim 
717b17bd5dbSJonathan Kim 	*entry_size = min_t(size_t, *entry_size, sizeof(struct kfd_queue_snapshot_entry));
718b17bd5dbSJonathan Kim 	mutex_lock(&pqm->process->event_mutex);
719b17bd5dbSJonathan Kim 
720b17bd5dbSJonathan Kim 	memset(&src, 0, sizeof(src));
721b17bd5dbSJonathan Kim 
722b17bd5dbSJonathan Kim 	list_for_each_entry(pqn, &pqm->queues, process_queue_list) {
723b17bd5dbSJonathan Kim 		if (!pqn->q)
724b17bd5dbSJonathan Kim 			continue;
725b17bd5dbSJonathan Kim 
726b17bd5dbSJonathan Kim 		if (*num_qss_entries < tmp_qss_entries) {
727b17bd5dbSJonathan Kim 			set_queue_snapshot_entry(pqn->q, exception_clear_mask, &src);
728b17bd5dbSJonathan Kim 
729b17bd5dbSJonathan Kim 			if (copy_to_user(buf, &src, *entry_size)) {
730b17bd5dbSJonathan Kim 				r = -EFAULT;
731b17bd5dbSJonathan Kim 				break;
732b17bd5dbSJonathan Kim 			}
733b17bd5dbSJonathan Kim 			buf += tmp_entry_size;
734b17bd5dbSJonathan Kim 		}
735b17bd5dbSJonathan Kim 		*num_qss_entries += 1;
736b17bd5dbSJonathan Kim 	}
737b17bd5dbSJonathan Kim 
738b17bd5dbSJonathan Kim 	mutex_unlock(&pqm->process->event_mutex);
739b17bd5dbSJonathan Kim 	return r;
740b17bd5dbSJonathan Kim }
741b17bd5dbSJonathan Kim 
get_queue_data_sizes(struct kfd_process_device * pdd,struct queue * q,uint32_t * mqd_size,uint32_t * ctl_stack_size)7423a9822d7SDavid Yat Sin static int get_queue_data_sizes(struct kfd_process_device *pdd,
7433a9822d7SDavid Yat Sin 				struct queue *q,
7443a9822d7SDavid Yat Sin 				uint32_t *mqd_size,
7453a9822d7SDavid Yat Sin 				uint32_t *ctl_stack_size)
74642c6c482SDavid Yat Sin {
74742c6c482SDavid Yat Sin 	int ret;
74842c6c482SDavid Yat Sin 
7493a9822d7SDavid Yat Sin 	ret = pqm_get_queue_checkpoint_info(&pdd->process->pqm,
7503a9822d7SDavid Yat Sin 					    q->properties.queue_id,
7513a9822d7SDavid Yat Sin 					    mqd_size,
7523a9822d7SDavid Yat Sin 					    ctl_stack_size);
75342c6c482SDavid Yat Sin 	if (ret)
75442c6c482SDavid Yat Sin 		pr_err("Failed to get queue dump info (%d)\n", ret);
75542c6c482SDavid Yat Sin 
75642c6c482SDavid Yat Sin 	return ret;
75742c6c482SDavid Yat Sin }
75842c6c482SDavid Yat Sin 
kfd_process_get_queue_info(struct kfd_process * p,uint32_t * num_queues,uint64_t * priv_data_sizes)759626f7b31SDavid Yat Sin int kfd_process_get_queue_info(struct kfd_process *p,
760626f7b31SDavid Yat Sin 			       uint32_t *num_queues,
761626f7b31SDavid Yat Sin 			       uint64_t *priv_data_sizes)
762626f7b31SDavid Yat Sin {
76342c6c482SDavid Yat Sin 	uint32_t extra_data_sizes = 0;
764626f7b31SDavid Yat Sin 	struct queue *q;
765626f7b31SDavid Yat Sin 	int i;
76642c6c482SDavid Yat Sin 	int ret;
767626f7b31SDavid Yat Sin 
768626f7b31SDavid Yat Sin 	*num_queues = 0;
769626f7b31SDavid Yat Sin 
770626f7b31SDavid Yat Sin 	/* Run over all PDDs of the process */
771626f7b31SDavid Yat Sin 	for (i = 0; i < p->n_pdds; i++) {
772626f7b31SDavid Yat Sin 		struct kfd_process_device *pdd = p->pdds[i];
773626f7b31SDavid Yat Sin 
774626f7b31SDavid Yat Sin 		list_for_each_entry(q, &pdd->qpd.queues_list, list) {
775626f7b31SDavid Yat Sin 			if (q->properties.type == KFD_QUEUE_TYPE_COMPUTE ||
776626f7b31SDavid Yat Sin 				q->properties.type == KFD_QUEUE_TYPE_SDMA ||
777626f7b31SDavid Yat Sin 				q->properties.type == KFD_QUEUE_TYPE_SDMA_XGMI) {
7783a9822d7SDavid Yat Sin 				uint32_t mqd_size, ctl_stack_size;
7793a9822d7SDavid Yat Sin 
780626f7b31SDavid Yat Sin 				*num_queues = *num_queues + 1;
78142c6c482SDavid Yat Sin 
7823a9822d7SDavid Yat Sin 				ret = get_queue_data_sizes(pdd, q, &mqd_size, &ctl_stack_size);
78342c6c482SDavid Yat Sin 				if (ret)
78442c6c482SDavid Yat Sin 					return ret;
78542c6c482SDavid Yat Sin 
7863a9822d7SDavid Yat Sin 				extra_data_sizes += mqd_size + ctl_stack_size;
787626f7b31SDavid Yat Sin 			} else {
788626f7b31SDavid Yat Sin 				pr_err("Unsupported queue type (%d)\n", q->properties.type);
789626f7b31SDavid Yat Sin 				return -EOPNOTSUPP;
790626f7b31SDavid Yat Sin 			}
791626f7b31SDavid Yat Sin 		}
792626f7b31SDavid Yat Sin 	}
79342c6c482SDavid Yat Sin 	*priv_data_sizes = extra_data_sizes +
79442c6c482SDavid Yat Sin 				(*num_queues * sizeof(struct kfd_criu_queue_priv_data));
795626f7b31SDavid Yat Sin 
796626f7b31SDavid Yat Sin 	return 0;
797626f7b31SDavid Yat Sin }
798626f7b31SDavid Yat Sin 
pqm_checkpoint_mqd(struct process_queue_manager * pqm,unsigned int qid,void * mqd,void * ctl_stack)7993a9822d7SDavid Yat Sin static int pqm_checkpoint_mqd(struct process_queue_manager *pqm,
8003a9822d7SDavid Yat Sin 			      unsigned int qid,
8013a9822d7SDavid Yat Sin 			      void *mqd,
8023a9822d7SDavid Yat Sin 			      void *ctl_stack)
80342c6c482SDavid Yat Sin {
80442c6c482SDavid Yat Sin 	struct process_queue_node *pqn;
80542c6c482SDavid Yat Sin 
80642c6c482SDavid Yat Sin 	pqn = get_queue_by_qid(pqm, qid);
80742c6c482SDavid Yat Sin 	if (!pqn) {
80842c6c482SDavid Yat Sin 		pr_debug("amdkfd: No queue %d exists for operation\n", qid);
80942c6c482SDavid Yat Sin 		return -EFAULT;
81042c6c482SDavid Yat Sin 	}
81142c6c482SDavid Yat Sin 
81242c6c482SDavid Yat Sin 	if (!pqn->q->device->dqm->ops.checkpoint_mqd) {
81342c6c482SDavid Yat Sin 		pr_err("amdkfd: queue dumping not supported on this device\n");
81442c6c482SDavid Yat Sin 		return -EOPNOTSUPP;
81542c6c482SDavid Yat Sin 	}
81642c6c482SDavid Yat Sin 
8173a9822d7SDavid Yat Sin 	return pqn->q->device->dqm->ops.checkpoint_mqd(pqn->q->device->dqm,
8183a9822d7SDavid Yat Sin 						       pqn->q, mqd, ctl_stack);
81942c6c482SDavid Yat Sin }
82042c6c482SDavid Yat Sin 
criu_checkpoint_queue(struct kfd_process_device * pdd,struct queue * q,struct kfd_criu_queue_priv_data * q_data)82142c6c482SDavid Yat Sin static int criu_checkpoint_queue(struct kfd_process_device *pdd,
822626f7b31SDavid Yat Sin 			   struct queue *q,
823626f7b31SDavid Yat Sin 			   struct kfd_criu_queue_priv_data *q_data)
824626f7b31SDavid Yat Sin {
8253a9822d7SDavid Yat Sin 	uint8_t *mqd, *ctl_stack;
82642c6c482SDavid Yat Sin 	int ret;
82742c6c482SDavid Yat Sin 
82842c6c482SDavid Yat Sin 	mqd = (void *)(q_data + 1);
8293a9822d7SDavid Yat Sin 	ctl_stack = mqd + q_data->mqd_size;
83042c6c482SDavid Yat Sin 
831bef153b7SDavid Yat Sin 	q_data->gpu_id = pdd->user_gpu_id;
832626f7b31SDavid Yat Sin 	q_data->type = q->properties.type;
833626f7b31SDavid Yat Sin 	q_data->format = q->properties.format;
834626f7b31SDavid Yat Sin 	q_data->q_id =  q->properties.queue_id;
835626f7b31SDavid Yat Sin 	q_data->q_address = q->properties.queue_address;
836626f7b31SDavid Yat Sin 	q_data->q_size = q->properties.queue_size;
837626f7b31SDavid Yat Sin 	q_data->priority = q->properties.priority;
838626f7b31SDavid Yat Sin 	q_data->q_percent = q->properties.queue_percent;
839626f7b31SDavid Yat Sin 	q_data->read_ptr_addr = (uint64_t)q->properties.read_ptr;
840626f7b31SDavid Yat Sin 	q_data->write_ptr_addr = (uint64_t)q->properties.write_ptr;
841626f7b31SDavid Yat Sin 	q_data->doorbell_id = q->doorbell_id;
842626f7b31SDavid Yat Sin 
843626f7b31SDavid Yat Sin 	q_data->sdma_id = q->sdma_id;
844626f7b31SDavid Yat Sin 
845626f7b31SDavid Yat Sin 	q_data->eop_ring_buffer_address =
846626f7b31SDavid Yat Sin 		q->properties.eop_ring_buffer_address;
847626f7b31SDavid Yat Sin 
848626f7b31SDavid Yat Sin 	q_data->eop_ring_buffer_size = q->properties.eop_ring_buffer_size;
849626f7b31SDavid Yat Sin 
850626f7b31SDavid Yat Sin 	q_data->ctx_save_restore_area_address =
851626f7b31SDavid Yat Sin 		q->properties.ctx_save_restore_area_address;
852626f7b31SDavid Yat Sin 
853626f7b31SDavid Yat Sin 	q_data->ctx_save_restore_area_size =
854626f7b31SDavid Yat Sin 		q->properties.ctx_save_restore_area_size;
855626f7b31SDavid Yat Sin 
856747eea07SDavid Yat Sin 	q_data->gws = !!q->gws;
857747eea07SDavid Yat Sin 
8583a9822d7SDavid Yat Sin 	ret = pqm_checkpoint_mqd(&pdd->process->pqm, q->properties.queue_id, mqd, ctl_stack);
85942c6c482SDavid Yat Sin 	if (ret) {
86042c6c482SDavid Yat Sin 		pr_err("Failed checkpoint queue_mqd (%d)\n", ret);
86142c6c482SDavid Yat Sin 		return ret;
86242c6c482SDavid Yat Sin 	}
86342c6c482SDavid Yat Sin 
864626f7b31SDavid Yat Sin 	pr_debug("Dumping Queue: gpu_id:%x queue_id:%u\n", q_data->gpu_id, q_data->q_id);
86542c6c482SDavid Yat Sin 	return ret;
866626f7b31SDavid Yat Sin }
867626f7b31SDavid Yat Sin 
criu_checkpoint_queues_device(struct kfd_process_device * pdd,uint8_t __user * user_priv,unsigned int * q_index,uint64_t * queues_priv_data_offset)8688668dfc3SDavid Yat Sin static int criu_checkpoint_queues_device(struct kfd_process_device *pdd,
869626f7b31SDavid Yat Sin 				   uint8_t __user *user_priv,
870626f7b31SDavid Yat Sin 				   unsigned int *q_index,
871626f7b31SDavid Yat Sin 				   uint64_t *queues_priv_data_offset)
872626f7b31SDavid Yat Sin {
87342c6c482SDavid Yat Sin 	unsigned int q_private_data_size = 0;
87442c6c482SDavid Yat Sin 	uint8_t *q_private_data = NULL; /* Local buffer to store individual queue private data */
875626f7b31SDavid Yat Sin 	struct queue *q;
876626f7b31SDavid Yat Sin 	int ret = 0;
877626f7b31SDavid Yat Sin 
878626f7b31SDavid Yat Sin 	list_for_each_entry(q, &pdd->qpd.queues_list, list) {
87942c6c482SDavid Yat Sin 		struct kfd_criu_queue_priv_data *q_data;
88042c6c482SDavid Yat Sin 		uint64_t q_data_size;
88142c6c482SDavid Yat Sin 		uint32_t mqd_size;
8823a9822d7SDavid Yat Sin 		uint32_t ctl_stack_size;
88342c6c482SDavid Yat Sin 
884626f7b31SDavid Yat Sin 		if (q->properties.type != KFD_QUEUE_TYPE_COMPUTE &&
885626f7b31SDavid Yat Sin 			q->properties.type != KFD_QUEUE_TYPE_SDMA &&
886626f7b31SDavid Yat Sin 			q->properties.type != KFD_QUEUE_TYPE_SDMA_XGMI) {
887626f7b31SDavid Yat Sin 
888626f7b31SDavid Yat Sin 			pr_err("Unsupported queue type (%d)\n", q->properties.type);
889626f7b31SDavid Yat Sin 			ret = -EOPNOTSUPP;
890626f7b31SDavid Yat Sin 			break;
891626f7b31SDavid Yat Sin 		}
892626f7b31SDavid Yat Sin 
8933a9822d7SDavid Yat Sin 		ret = get_queue_data_sizes(pdd, q, &mqd_size, &ctl_stack_size);
89442c6c482SDavid Yat Sin 		if (ret)
89542c6c482SDavid Yat Sin 			break;
89642c6c482SDavid Yat Sin 
8973a9822d7SDavid Yat Sin 		q_data_size = sizeof(*q_data) + mqd_size + ctl_stack_size;
89842c6c482SDavid Yat Sin 
89942c6c482SDavid Yat Sin 		/* Increase local buffer space if needed */
90042c6c482SDavid Yat Sin 		if (q_private_data_size < q_data_size) {
90142c6c482SDavid Yat Sin 			kfree(q_private_data);
90242c6c482SDavid Yat Sin 
90342c6c482SDavid Yat Sin 			q_private_data = kzalloc(q_data_size, GFP_KERNEL);
90442c6c482SDavid Yat Sin 			if (!q_private_data) {
90542c6c482SDavid Yat Sin 				ret = -ENOMEM;
90642c6c482SDavid Yat Sin 				break;
90742c6c482SDavid Yat Sin 			}
90842c6c482SDavid Yat Sin 			q_private_data_size = q_data_size;
90942c6c482SDavid Yat Sin 		}
91042c6c482SDavid Yat Sin 
91142c6c482SDavid Yat Sin 		q_data = (struct kfd_criu_queue_priv_data *)q_private_data;
91242c6c482SDavid Yat Sin 
9133a9822d7SDavid Yat Sin 		/* data stored in this order: priv_data, mqd, ctl_stack */
91442c6c482SDavid Yat Sin 		q_data->mqd_size = mqd_size;
9153a9822d7SDavid Yat Sin 		q_data->ctl_stack_size = ctl_stack_size;
91642c6c482SDavid Yat Sin 
91742c6c482SDavid Yat Sin 		ret = criu_checkpoint_queue(pdd, q, q_data);
91842c6c482SDavid Yat Sin 		if (ret)
91942c6c482SDavid Yat Sin 			break;
92042c6c482SDavid Yat Sin 
9218668dfc3SDavid Yat Sin 		q_data->object_type = KFD_CRIU_OBJECT_TYPE_QUEUE;
922626f7b31SDavid Yat Sin 
92342c6c482SDavid Yat Sin 		ret = copy_to_user(user_priv + *queues_priv_data_offset,
92442c6c482SDavid Yat Sin 				q_data, q_data_size);
925626f7b31SDavid Yat Sin 		if (ret) {
926626f7b31SDavid Yat Sin 			ret = -EFAULT;
927626f7b31SDavid Yat Sin 			break;
928626f7b31SDavid Yat Sin 		}
92942c6c482SDavid Yat Sin 		*queues_priv_data_offset += q_data_size;
930626f7b31SDavid Yat Sin 		*q_index = *q_index + 1;
931626f7b31SDavid Yat Sin 	}
932626f7b31SDavid Yat Sin 
93342c6c482SDavid Yat Sin 	kfree(q_private_data);
934626f7b31SDavid Yat Sin 
935626f7b31SDavid Yat Sin 	return ret;
936626f7b31SDavid Yat Sin }
937626f7b31SDavid Yat Sin 
kfd_criu_checkpoint_queues(struct kfd_process * p,uint8_t __user * user_priv_data,uint64_t * priv_data_offset)938626f7b31SDavid Yat Sin int kfd_criu_checkpoint_queues(struct kfd_process *p,
939626f7b31SDavid Yat Sin 			 uint8_t __user *user_priv_data,
940626f7b31SDavid Yat Sin 			 uint64_t *priv_data_offset)
941626f7b31SDavid Yat Sin {
942626f7b31SDavid Yat Sin 	int ret = 0, pdd_index, q_index = 0;
943626f7b31SDavid Yat Sin 
944626f7b31SDavid Yat Sin 	for (pdd_index = 0; pdd_index < p->n_pdds; pdd_index++) {
945626f7b31SDavid Yat Sin 		struct kfd_process_device *pdd = p->pdds[pdd_index];
946626f7b31SDavid Yat Sin 
947626f7b31SDavid Yat Sin 		/*
9488668dfc3SDavid Yat Sin 		 * criu_checkpoint_queues_device will copy data to user and update q_index and
949626f7b31SDavid Yat Sin 		 * queues_priv_data_offset
950626f7b31SDavid Yat Sin 		 */
9518668dfc3SDavid Yat Sin 		ret = criu_checkpoint_queues_device(pdd, user_priv_data, &q_index,
9528668dfc3SDavid Yat Sin 					      priv_data_offset);
9538668dfc3SDavid Yat Sin 
954626f7b31SDavid Yat Sin 		if (ret)
955626f7b31SDavid Yat Sin 			break;
956626f7b31SDavid Yat Sin 	}
957626f7b31SDavid Yat Sin 
958626f7b31SDavid Yat Sin 	return ret;
959626f7b31SDavid Yat Sin }
960626f7b31SDavid Yat Sin 
set_queue_properties_from_criu(struct queue_properties * qp,struct kfd_criu_queue_priv_data * q_data)961626f7b31SDavid Yat Sin static void set_queue_properties_from_criu(struct queue_properties *qp,
962626f7b31SDavid Yat Sin 					  struct kfd_criu_queue_priv_data *q_data)
963626f7b31SDavid Yat Sin {
964626f7b31SDavid Yat Sin 	qp->is_interop = false;
965626f7b31SDavid Yat Sin 	qp->queue_percent = q_data->q_percent;
966626f7b31SDavid Yat Sin 	qp->priority = q_data->priority;
967626f7b31SDavid Yat Sin 	qp->queue_address = q_data->q_address;
968626f7b31SDavid Yat Sin 	qp->queue_size = q_data->q_size;
969626f7b31SDavid Yat Sin 	qp->read_ptr = (uint32_t *) q_data->read_ptr_addr;
970626f7b31SDavid Yat Sin 	qp->write_ptr = (uint32_t *) q_data->write_ptr_addr;
971626f7b31SDavid Yat Sin 	qp->eop_ring_buffer_address = q_data->eop_ring_buffer_address;
972626f7b31SDavid Yat Sin 	qp->eop_ring_buffer_size = q_data->eop_ring_buffer_size;
973626f7b31SDavid Yat Sin 	qp->ctx_save_restore_area_address = q_data->ctx_save_restore_area_address;
974626f7b31SDavid Yat Sin 	qp->ctx_save_restore_area_size = q_data->ctx_save_restore_area_size;
975626f7b31SDavid Yat Sin 	qp->ctl_stack_size = q_data->ctl_stack_size;
976626f7b31SDavid Yat Sin 	qp->type = q_data->type;
977626f7b31SDavid Yat Sin 	qp->format = q_data->format;
978626f7b31SDavid Yat Sin }
979626f7b31SDavid Yat Sin 
kfd_criu_restore_queue(struct kfd_process * p,uint8_t __user * user_priv_ptr,uint64_t * priv_data_offset,uint64_t max_priv_data_size)980626f7b31SDavid Yat Sin int kfd_criu_restore_queue(struct kfd_process *p,
981626f7b31SDavid Yat Sin 			   uint8_t __user *user_priv_ptr,
982626f7b31SDavid Yat Sin 			   uint64_t *priv_data_offset,
983626f7b31SDavid Yat Sin 			   uint64_t max_priv_data_size)
984626f7b31SDavid Yat Sin {
9853a9822d7SDavid Yat Sin 	uint8_t *mqd, *ctl_stack, *q_extra_data = NULL;
986626f7b31SDavid Yat Sin 	struct kfd_criu_queue_priv_data *q_data;
987626f7b31SDavid Yat Sin 	struct kfd_process_device *pdd;
98842c6c482SDavid Yat Sin 	uint64_t q_extra_data_size;
989626f7b31SDavid Yat Sin 	struct queue_properties qp;
990626f7b31SDavid Yat Sin 	unsigned int queue_id;
991626f7b31SDavid Yat Sin 	int ret = 0;
992626f7b31SDavid Yat Sin 
993626f7b31SDavid Yat Sin 	if (*priv_data_offset + sizeof(*q_data) > max_priv_data_size)
994626f7b31SDavid Yat Sin 		return -EINVAL;
995626f7b31SDavid Yat Sin 
996626f7b31SDavid Yat Sin 	q_data = kmalloc(sizeof(*q_data), GFP_KERNEL);
997626f7b31SDavid Yat Sin 	if (!q_data)
998626f7b31SDavid Yat Sin 		return -ENOMEM;
999626f7b31SDavid Yat Sin 
1000626f7b31SDavid Yat Sin 	ret = copy_from_user(q_data, user_priv_ptr + *priv_data_offset, sizeof(*q_data));
1001626f7b31SDavid Yat Sin 	if (ret) {
1002626f7b31SDavid Yat Sin 		ret = -EFAULT;
1003626f7b31SDavid Yat Sin 		goto exit;
1004626f7b31SDavid Yat Sin 	}
1005626f7b31SDavid Yat Sin 
1006626f7b31SDavid Yat Sin 	*priv_data_offset += sizeof(*q_data);
10078f7519b2SDavid Yat Sin 	q_extra_data_size = (uint64_t)q_data->ctl_stack_size + q_data->mqd_size;
100842c6c482SDavid Yat Sin 
100942c6c482SDavid Yat Sin 	if (*priv_data_offset + q_extra_data_size > max_priv_data_size) {
101042c6c482SDavid Yat Sin 		ret = -EINVAL;
101142c6c482SDavid Yat Sin 		goto exit;
101242c6c482SDavid Yat Sin 	}
101342c6c482SDavid Yat Sin 
101442c6c482SDavid Yat Sin 	q_extra_data = kmalloc(q_extra_data_size, GFP_KERNEL);
101542c6c482SDavid Yat Sin 	if (!q_extra_data) {
101642c6c482SDavid Yat Sin 		ret = -ENOMEM;
101742c6c482SDavid Yat Sin 		goto exit;
101842c6c482SDavid Yat Sin 	}
101942c6c482SDavid Yat Sin 
102042c6c482SDavid Yat Sin 	ret = copy_from_user(q_extra_data, user_priv_ptr + *priv_data_offset, q_extra_data_size);
102142c6c482SDavid Yat Sin 	if (ret) {
102242c6c482SDavid Yat Sin 		ret = -EFAULT;
102342c6c482SDavid Yat Sin 		goto exit;
102442c6c482SDavid Yat Sin 	}
102542c6c482SDavid Yat Sin 
102642c6c482SDavid Yat Sin 	*priv_data_offset += q_extra_data_size;
1027626f7b31SDavid Yat Sin 
1028bef153b7SDavid Yat Sin 	pdd = kfd_process_device_data_by_id(p, q_data->gpu_id);
1029626f7b31SDavid Yat Sin 	if (!pdd) {
1030626f7b31SDavid Yat Sin 		pr_err("Failed to get pdd\n");
1031bef153b7SDavid Yat Sin 		ret = -EINVAL;
1032bef153b7SDavid Yat Sin 		goto exit;
1033626f7b31SDavid Yat Sin 	}
1034a4a3798fSRajneesh Bhardwaj 
10353a9822d7SDavid Yat Sin 	/* data stored in this order: mqd, ctl_stack */
103642c6c482SDavid Yat Sin 	mqd = q_extra_data;
10373a9822d7SDavid Yat Sin 	ctl_stack = mqd + q_data->mqd_size;
1038626f7b31SDavid Yat Sin 
1039626f7b31SDavid Yat Sin 	memset(&qp, 0, sizeof(qp));
1040626f7b31SDavid Yat Sin 	set_queue_properties_from_criu(&qp, q_data);
1041626f7b31SDavid Yat Sin 
1042626f7b31SDavid Yat Sin 	print_queue_properties(&qp);
1043626f7b31SDavid Yat Sin 
1044bc4688aeSLang Yu 	ret = pqm_create_queue(&p->pqm, pdd->dev, &qp, &queue_id, q_data, mqd, ctl_stack, NULL);
1045626f7b31SDavid Yat Sin 	if (ret) {
1046626f7b31SDavid Yat Sin 		pr_err("Failed to create new queue err:%d\n", ret);
1047747eea07SDavid Yat Sin 		goto exit;
1048626f7b31SDavid Yat Sin 	}
1049626f7b31SDavid Yat Sin 
1050747eea07SDavid Yat Sin 	if (q_data->gws)
1051747eea07SDavid Yat Sin 		ret = pqm_set_gws(&p->pqm, q_data->q_id, pdd->dev->gws);
1052747eea07SDavid Yat Sin 
1053626f7b31SDavid Yat Sin exit:
1054626f7b31SDavid Yat Sin 	if (ret)
1055747eea07SDavid Yat Sin 		pr_err("Failed to restore queue (%d)\n", ret);
1056626f7b31SDavid Yat Sin 	else
1057626f7b31SDavid Yat Sin 		pr_debug("Queue id %d was restored successfully\n", queue_id);
1058626f7b31SDavid Yat Sin 
1059626f7b31SDavid Yat Sin 	kfree(q_data);
1060aa47fe8dSJesse Zhang 	kfree(q_extra_data);
1061626f7b31SDavid Yat Sin 
1062626f7b31SDavid Yat Sin 	return ret;
1063626f7b31SDavid Yat Sin }
1064626f7b31SDavid Yat Sin 
pqm_get_queue_checkpoint_info(struct process_queue_manager * pqm,unsigned int qid,uint32_t * mqd_size,uint32_t * ctl_stack_size)106542c6c482SDavid Yat Sin int pqm_get_queue_checkpoint_info(struct process_queue_manager *pqm,
106642c6c482SDavid Yat Sin 				  unsigned int qid,
10673a9822d7SDavid Yat Sin 				  uint32_t *mqd_size,
10683a9822d7SDavid Yat Sin 				  uint32_t *ctl_stack_size)
106942c6c482SDavid Yat Sin {
107042c6c482SDavid Yat Sin 	struct process_queue_node *pqn;
107142c6c482SDavid Yat Sin 
107242c6c482SDavid Yat Sin 	pqn = get_queue_by_qid(pqm, qid);
107342c6c482SDavid Yat Sin 	if (!pqn) {
107442c6c482SDavid Yat Sin 		pr_debug("amdkfd: No queue %d exists for operation\n", qid);
107542c6c482SDavid Yat Sin 		return -EFAULT;
107642c6c482SDavid Yat Sin 	}
107742c6c482SDavid Yat Sin 
107842c6c482SDavid Yat Sin 	if (!pqn->q->device->dqm->ops.get_queue_checkpoint_info) {
107942c6c482SDavid Yat Sin 		pr_err("amdkfd: queue dumping not supported on this device\n");
108042c6c482SDavid Yat Sin 		return -EOPNOTSUPP;
108142c6c482SDavid Yat Sin 	}
108242c6c482SDavid Yat Sin 
10833a9822d7SDavid Yat Sin 	pqn->q->device->dqm->ops.get_queue_checkpoint_info(pqn->q->device->dqm,
10843a9822d7SDavid Yat Sin 						       pqn->q, mqd_size,
10853a9822d7SDavid Yat Sin 						       ctl_stack_size);
108642c6c482SDavid Yat Sin 	return 0;
108742c6c482SDavid Yat Sin }
108842c6c482SDavid Yat Sin 
1089851a645eSFelix Kuehling #if defined(CONFIG_DEBUG_FS)
109045102048SBen Goz 
pqm_debugfs_mqds(struct seq_file * m,void * data)1091851a645eSFelix Kuehling int pqm_debugfs_mqds(struct seq_file *m, void *data)
1092851a645eSFelix Kuehling {
1093851a645eSFelix Kuehling 	struct process_queue_manager *pqm = data;
1094851a645eSFelix Kuehling 	struct process_queue_node *pqn;
1095851a645eSFelix Kuehling 	struct queue *q;
1096851a645eSFelix Kuehling 	enum KFD_MQD_TYPE mqd_type;
10978d5f3552SYong Zhao 	struct mqd_manager *mqd_mgr;
10982f77b9a2SMukul Joshi 	int r = 0, xcc, num_xccs = 1;
10992f77b9a2SMukul Joshi 	void *mqd;
11002f77b9a2SMukul Joshi 	uint64_t size = 0;
1101851a645eSFelix Kuehling 
1102851a645eSFelix Kuehling 	list_for_each_entry(pqn, &pqm->queues, process_queue_list) {
1103851a645eSFelix Kuehling 		if (pqn->q) {
1104851a645eSFelix Kuehling 			q = pqn->q;
1105851a645eSFelix Kuehling 			switch (q->properties.type) {
1106851a645eSFelix Kuehling 			case KFD_QUEUE_TYPE_SDMA:
11071b4670f6SOak Zeng 			case KFD_QUEUE_TYPE_SDMA_XGMI:
1108851a645eSFelix Kuehling 				seq_printf(m, "  SDMA queue on device %x\n",
1109851a645eSFelix Kuehling 					   q->device->id);
1110851a645eSFelix Kuehling 				mqd_type = KFD_MQD_TYPE_SDMA;
1111851a645eSFelix Kuehling 				break;
1112851a645eSFelix Kuehling 			case KFD_QUEUE_TYPE_COMPUTE:
1113851a645eSFelix Kuehling 				seq_printf(m, "  Compute queue on device %x\n",
1114851a645eSFelix Kuehling 					   q->device->id);
1115851a645eSFelix Kuehling 				mqd_type = KFD_MQD_TYPE_CP;
1116c4050ff1SLijo Lazar 				num_xccs = NUM_XCC(q->device->xcc_mask);
1117851a645eSFelix Kuehling 				break;
1118851a645eSFelix Kuehling 			default:
1119851a645eSFelix Kuehling 				seq_printf(m,
1120851a645eSFelix Kuehling 				"  Bad user queue type %d on device %x\n",
1121851a645eSFelix Kuehling 					   q->properties.type, q->device->id);
1122851a645eSFelix Kuehling 				continue;
1123851a645eSFelix Kuehling 			}
1124fdfa090bSOak Zeng 			mqd_mgr = q->device->dqm->mqd_mgrs[mqd_type];
11252f77b9a2SMukul Joshi 			size = mqd_mgr->mqd_stride(mqd_mgr,
11262f77b9a2SMukul Joshi 							&q->properties);
1127851a645eSFelix Kuehling 		} else if (pqn->kq) {
1128851a645eSFelix Kuehling 			q = pqn->kq->queue;
11298d5f3552SYong Zhao 			mqd_mgr = pqn->kq->mqd_mgr;
1130851a645eSFelix Kuehling 			switch (q->properties.type) {
1131851a645eSFelix Kuehling 			case KFD_QUEUE_TYPE_DIQ:
1132851a645eSFelix Kuehling 				seq_printf(m, "  DIQ on device %x\n",
1133851a645eSFelix Kuehling 					   pqn->kq->dev->id);
1134851a645eSFelix Kuehling 				break;
1135851a645eSFelix Kuehling 			default:
1136851a645eSFelix Kuehling 				seq_printf(m,
1137851a645eSFelix Kuehling 				"  Bad kernel queue type %d on device %x\n",
1138851a645eSFelix Kuehling 					   q->properties.type,
1139851a645eSFelix Kuehling 					   pqn->kq->dev->id);
1140851a645eSFelix Kuehling 				continue;
1141851a645eSFelix Kuehling 			}
1142851a645eSFelix Kuehling 		} else {
1143851a645eSFelix Kuehling 			seq_printf(m,
1144851a645eSFelix Kuehling 		"  Weird: Queue node with neither kernel nor user queue\n");
1145851a645eSFelix Kuehling 			continue;
1146851a645eSFelix Kuehling 		}
1147851a645eSFelix Kuehling 
11482f77b9a2SMukul Joshi 		for (xcc = 0; xcc < num_xccs; xcc++) {
11492f77b9a2SMukul Joshi 			mqd = q->mqd + size * xcc;
11502f77b9a2SMukul Joshi 			r = mqd_mgr->debugfs_show_mqd(m, mqd);
1151851a645eSFelix Kuehling 			if (r != 0)
1152851a645eSFelix Kuehling 				break;
1153851a645eSFelix Kuehling 		}
11542f77b9a2SMukul Joshi 	}
1155851a645eSFelix Kuehling 
1156851a645eSFelix Kuehling 	return r;
1157851a645eSFelix Kuehling }
1158851a645eSFelix Kuehling 
1159851a645eSFelix Kuehling #endif
1160