xref: /linux/drivers/gpu/drm/imagination/pvr_context.c (revision 0ea5c948cb64bab5bc7a5516774eb8536f05aa0d)
1d2d79d29SSarah Walker // SPDX-License-Identifier: GPL-2.0-only OR MIT
2d2d79d29SSarah Walker /* Copyright (c) 2023 Imagination Technologies Ltd. */
3d2d79d29SSarah Walker 
4d2d79d29SSarah Walker #include "pvr_cccb.h"
5d2d79d29SSarah Walker #include "pvr_context.h"
6d2d79d29SSarah Walker #include "pvr_device.h"
7d2d79d29SSarah Walker #include "pvr_drv.h"
8d2d79d29SSarah Walker #include "pvr_gem.h"
9*eaf01ee5SSarah Walker #include "pvr_job.h"
10d2d79d29SSarah Walker #include "pvr_power.h"
11d2d79d29SSarah Walker #include "pvr_rogue_fwif.h"
12d2d79d29SSarah Walker #include "pvr_rogue_fwif_common.h"
13d2d79d29SSarah Walker #include "pvr_rogue_fwif_resetframework.h"
14*eaf01ee5SSarah Walker #include "pvr_stream.h"
15d2d79d29SSarah Walker #include "pvr_stream_defs.h"
16d2d79d29SSarah Walker #include "pvr_vm.h"
17d2d79d29SSarah Walker 
18d2d79d29SSarah Walker #include <drm/drm_auth.h>
19d2d79d29SSarah Walker #include <drm/drm_managed.h>
20d2d79d29SSarah Walker #include <linux/errno.h>
21d2d79d29SSarah Walker #include <linux/kernel.h>
22d2d79d29SSarah Walker #include <linux/sched.h>
23d2d79d29SSarah Walker #include <linux/slab.h>
24d2d79d29SSarah Walker #include <linux/string.h>
25d2d79d29SSarah Walker #include <linux/types.h>
26d2d79d29SSarah Walker #include <linux/xarray.h>
27d2d79d29SSarah Walker 
28d2d79d29SSarah Walker static int
29d2d79d29SSarah Walker remap_priority(struct pvr_file *pvr_file, s32 uapi_priority,
30d2d79d29SSarah Walker 	       enum pvr_context_priority *priority_out)
31d2d79d29SSarah Walker {
32d2d79d29SSarah Walker 	switch (uapi_priority) {
33d2d79d29SSarah Walker 	case DRM_PVR_CTX_PRIORITY_LOW:
34d2d79d29SSarah Walker 		*priority_out = PVR_CTX_PRIORITY_LOW;
35d2d79d29SSarah Walker 		break;
36d2d79d29SSarah Walker 	case DRM_PVR_CTX_PRIORITY_NORMAL:
37d2d79d29SSarah Walker 		*priority_out = PVR_CTX_PRIORITY_MEDIUM;
38d2d79d29SSarah Walker 		break;
39d2d79d29SSarah Walker 	case DRM_PVR_CTX_PRIORITY_HIGH:
40d2d79d29SSarah Walker 		if (!capable(CAP_SYS_NICE) && !drm_is_current_master(from_pvr_file(pvr_file)))
41d2d79d29SSarah Walker 			return -EACCES;
42d2d79d29SSarah Walker 		*priority_out = PVR_CTX_PRIORITY_HIGH;
43d2d79d29SSarah Walker 		break;
44d2d79d29SSarah Walker 	default:
45d2d79d29SSarah Walker 		return -EINVAL;
46d2d79d29SSarah Walker 	}
47d2d79d29SSarah Walker 
48d2d79d29SSarah Walker 	return 0;
49d2d79d29SSarah Walker }
50d2d79d29SSarah Walker 
51d2d79d29SSarah Walker static int get_fw_obj_size(enum drm_pvr_ctx_type type)
52d2d79d29SSarah Walker {
53d2d79d29SSarah Walker 	switch (type) {
54d2d79d29SSarah Walker 	case DRM_PVR_CTX_TYPE_RENDER:
55d2d79d29SSarah Walker 		return sizeof(struct rogue_fwif_fwrendercontext);
56d2d79d29SSarah Walker 	case DRM_PVR_CTX_TYPE_COMPUTE:
57d2d79d29SSarah Walker 		return sizeof(struct rogue_fwif_fwcomputecontext);
58d2d79d29SSarah Walker 	case DRM_PVR_CTX_TYPE_TRANSFER_FRAG:
59d2d79d29SSarah Walker 		return sizeof(struct rogue_fwif_fwtransfercontext);
60d2d79d29SSarah Walker 	}
61d2d79d29SSarah Walker 
62d2d79d29SSarah Walker 	return -EINVAL;
63d2d79d29SSarah Walker }
64d2d79d29SSarah Walker 
65d2d79d29SSarah Walker static int
66d2d79d29SSarah Walker process_static_context_state(struct pvr_device *pvr_dev, const struct pvr_stream_cmd_defs *cmd_defs,
67d2d79d29SSarah Walker 			     u64 stream_user_ptr, u32 stream_size, void *dest)
68d2d79d29SSarah Walker {
69d2d79d29SSarah Walker 	void *stream;
70d2d79d29SSarah Walker 	int err;
71d2d79d29SSarah Walker 
72d2d79d29SSarah Walker 	stream = kzalloc(stream_size, GFP_KERNEL);
73d2d79d29SSarah Walker 	if (!stream)
74d2d79d29SSarah Walker 		return -ENOMEM;
75d2d79d29SSarah Walker 
76d2d79d29SSarah Walker 	if (copy_from_user(stream, u64_to_user_ptr(stream_user_ptr), stream_size)) {
77d2d79d29SSarah Walker 		err = -EFAULT;
78d2d79d29SSarah Walker 		goto err_free;
79d2d79d29SSarah Walker 	}
80d2d79d29SSarah Walker 
81d2d79d29SSarah Walker 	err = pvr_stream_process(pvr_dev, cmd_defs, stream, stream_size, dest);
82d2d79d29SSarah Walker 	if (err)
83d2d79d29SSarah Walker 		goto err_free;
84d2d79d29SSarah Walker 
85d2d79d29SSarah Walker 	kfree(stream);
86d2d79d29SSarah Walker 
87d2d79d29SSarah Walker 	return 0;
88d2d79d29SSarah Walker 
89d2d79d29SSarah Walker err_free:
90d2d79d29SSarah Walker 	kfree(stream);
91d2d79d29SSarah Walker 
92d2d79d29SSarah Walker 	return err;
93d2d79d29SSarah Walker }
94d2d79d29SSarah Walker 
95d2d79d29SSarah Walker static int init_render_fw_objs(struct pvr_context *ctx,
96d2d79d29SSarah Walker 			       struct drm_pvr_ioctl_create_context_args *args,
97d2d79d29SSarah Walker 			       void *fw_ctx_map)
98d2d79d29SSarah Walker {
99d2d79d29SSarah Walker 	struct rogue_fwif_static_rendercontext_state *static_rendercontext_state;
100d2d79d29SSarah Walker 	struct rogue_fwif_fwrendercontext *fw_render_context = fw_ctx_map;
101d2d79d29SSarah Walker 
102d2d79d29SSarah Walker 	if (!args->static_context_state_len)
103d2d79d29SSarah Walker 		return -EINVAL;
104d2d79d29SSarah Walker 
105d2d79d29SSarah Walker 	static_rendercontext_state = &fw_render_context->static_render_context_state;
106d2d79d29SSarah Walker 
107d2d79d29SSarah Walker 	/* Copy static render context state from userspace. */
108d2d79d29SSarah Walker 	return process_static_context_state(ctx->pvr_dev,
109d2d79d29SSarah Walker 					    &pvr_static_render_context_state_stream,
110d2d79d29SSarah Walker 					    args->static_context_state,
111d2d79d29SSarah Walker 					    args->static_context_state_len,
112d2d79d29SSarah Walker 					    &static_rendercontext_state->ctxswitch_regs[0]);
113d2d79d29SSarah Walker }
114d2d79d29SSarah Walker 
115d2d79d29SSarah Walker static int init_compute_fw_objs(struct pvr_context *ctx,
116d2d79d29SSarah Walker 				struct drm_pvr_ioctl_create_context_args *args,
117d2d79d29SSarah Walker 				void *fw_ctx_map)
118d2d79d29SSarah Walker {
119d2d79d29SSarah Walker 	struct rogue_fwif_fwcomputecontext *fw_compute_context = fw_ctx_map;
120d2d79d29SSarah Walker 	struct rogue_fwif_cdm_registers_cswitch *ctxswitch_regs;
121d2d79d29SSarah Walker 
122d2d79d29SSarah Walker 	if (!args->static_context_state_len)
123d2d79d29SSarah Walker 		return -EINVAL;
124d2d79d29SSarah Walker 
125d2d79d29SSarah Walker 	ctxswitch_regs = &fw_compute_context->static_compute_context_state.ctxswitch_regs;
126d2d79d29SSarah Walker 
127d2d79d29SSarah Walker 	/* Copy static render context state from userspace. */
128d2d79d29SSarah Walker 	return process_static_context_state(ctx->pvr_dev,
129d2d79d29SSarah Walker 					    &pvr_static_compute_context_state_stream,
130d2d79d29SSarah Walker 					    args->static_context_state,
131d2d79d29SSarah Walker 					    args->static_context_state_len,
132d2d79d29SSarah Walker 					    ctxswitch_regs);
133d2d79d29SSarah Walker }
134d2d79d29SSarah Walker 
135d2d79d29SSarah Walker static int init_transfer_fw_objs(struct pvr_context *ctx,
136d2d79d29SSarah Walker 				 struct drm_pvr_ioctl_create_context_args *args,
137d2d79d29SSarah Walker 				 void *fw_ctx_map)
138d2d79d29SSarah Walker {
139d2d79d29SSarah Walker 	if (args->static_context_state_len)
140d2d79d29SSarah Walker 		return -EINVAL;
141d2d79d29SSarah Walker 
142d2d79d29SSarah Walker 	return 0;
143d2d79d29SSarah Walker }
144d2d79d29SSarah Walker 
145d2d79d29SSarah Walker static int init_fw_objs(struct pvr_context *ctx,
146d2d79d29SSarah Walker 			struct drm_pvr_ioctl_create_context_args *args,
147d2d79d29SSarah Walker 			void *fw_ctx_map)
148d2d79d29SSarah Walker {
149d2d79d29SSarah Walker 	switch (ctx->type) {
150d2d79d29SSarah Walker 	case DRM_PVR_CTX_TYPE_RENDER:
151d2d79d29SSarah Walker 		return init_render_fw_objs(ctx, args, fw_ctx_map);
152d2d79d29SSarah Walker 	case DRM_PVR_CTX_TYPE_COMPUTE:
153d2d79d29SSarah Walker 		return init_compute_fw_objs(ctx, args, fw_ctx_map);
154d2d79d29SSarah Walker 	case DRM_PVR_CTX_TYPE_TRANSFER_FRAG:
155d2d79d29SSarah Walker 		return init_transfer_fw_objs(ctx, args, fw_ctx_map);
156d2d79d29SSarah Walker 	}
157d2d79d29SSarah Walker 
158d2d79d29SSarah Walker 	return -EINVAL;
159d2d79d29SSarah Walker }
160d2d79d29SSarah Walker 
161d2d79d29SSarah Walker static void
162d2d79d29SSarah Walker ctx_fw_data_init(void *cpu_ptr, void *priv)
163d2d79d29SSarah Walker {
164d2d79d29SSarah Walker 	struct pvr_context *ctx = priv;
165d2d79d29SSarah Walker 
166d2d79d29SSarah Walker 	memcpy(cpu_ptr, ctx->data, ctx->data_size);
167d2d79d29SSarah Walker }
168d2d79d29SSarah Walker 
169d2d79d29SSarah Walker /**
170*eaf01ee5SSarah Walker  * pvr_context_destroy_queues() - Destroy all queues attached to a context.
171*eaf01ee5SSarah Walker  * @ctx: Context to destroy queues on.
172*eaf01ee5SSarah Walker  *
173*eaf01ee5SSarah Walker  * Should be called when the last reference to a context object is dropped.
174*eaf01ee5SSarah Walker  * It releases all resources attached to the queues bound to this context.
175*eaf01ee5SSarah Walker  */
176*eaf01ee5SSarah Walker static void pvr_context_destroy_queues(struct pvr_context *ctx)
177*eaf01ee5SSarah Walker {
178*eaf01ee5SSarah Walker 	switch (ctx->type) {
179*eaf01ee5SSarah Walker 	case DRM_PVR_CTX_TYPE_RENDER:
180*eaf01ee5SSarah Walker 		pvr_queue_destroy(ctx->queues.fragment);
181*eaf01ee5SSarah Walker 		pvr_queue_destroy(ctx->queues.geometry);
182*eaf01ee5SSarah Walker 		break;
183*eaf01ee5SSarah Walker 	case DRM_PVR_CTX_TYPE_COMPUTE:
184*eaf01ee5SSarah Walker 		pvr_queue_destroy(ctx->queues.compute);
185*eaf01ee5SSarah Walker 		break;
186*eaf01ee5SSarah Walker 	case DRM_PVR_CTX_TYPE_TRANSFER_FRAG:
187*eaf01ee5SSarah Walker 		pvr_queue_destroy(ctx->queues.transfer);
188*eaf01ee5SSarah Walker 		break;
189*eaf01ee5SSarah Walker 	}
190*eaf01ee5SSarah Walker }
191*eaf01ee5SSarah Walker 
192*eaf01ee5SSarah Walker /**
193*eaf01ee5SSarah Walker  * pvr_context_create_queues() - Create all queues attached to a context.
194*eaf01ee5SSarah Walker  * @ctx: Context to create queues on.
195*eaf01ee5SSarah Walker  * @args: Context creation arguments passed by userspace.
196*eaf01ee5SSarah Walker  * @fw_ctx_map: CPU mapping of the FW context object.
197*eaf01ee5SSarah Walker  *
198*eaf01ee5SSarah Walker  * Return:
199*eaf01ee5SSarah Walker  *  * 0 on success, or
200*eaf01ee5SSarah Walker  *  * A negative error code otherwise.
201*eaf01ee5SSarah Walker  */
202*eaf01ee5SSarah Walker static int pvr_context_create_queues(struct pvr_context *ctx,
203*eaf01ee5SSarah Walker 				     struct drm_pvr_ioctl_create_context_args *args,
204*eaf01ee5SSarah Walker 				     void *fw_ctx_map)
205*eaf01ee5SSarah Walker {
206*eaf01ee5SSarah Walker 	int err;
207*eaf01ee5SSarah Walker 
208*eaf01ee5SSarah Walker 	switch (ctx->type) {
209*eaf01ee5SSarah Walker 	case DRM_PVR_CTX_TYPE_RENDER:
210*eaf01ee5SSarah Walker 		ctx->queues.geometry = pvr_queue_create(ctx, DRM_PVR_JOB_TYPE_GEOMETRY,
211*eaf01ee5SSarah Walker 							args, fw_ctx_map);
212*eaf01ee5SSarah Walker 		if (IS_ERR(ctx->queues.geometry)) {
213*eaf01ee5SSarah Walker 			err = PTR_ERR(ctx->queues.geometry);
214*eaf01ee5SSarah Walker 			ctx->queues.geometry = NULL;
215*eaf01ee5SSarah Walker 			goto err_destroy_queues;
216*eaf01ee5SSarah Walker 		}
217*eaf01ee5SSarah Walker 
218*eaf01ee5SSarah Walker 		ctx->queues.fragment = pvr_queue_create(ctx, DRM_PVR_JOB_TYPE_FRAGMENT,
219*eaf01ee5SSarah Walker 							args, fw_ctx_map);
220*eaf01ee5SSarah Walker 		if (IS_ERR(ctx->queues.fragment)) {
221*eaf01ee5SSarah Walker 			err = PTR_ERR(ctx->queues.fragment);
222*eaf01ee5SSarah Walker 			ctx->queues.fragment = NULL;
223*eaf01ee5SSarah Walker 			goto err_destroy_queues;
224*eaf01ee5SSarah Walker 		}
225*eaf01ee5SSarah Walker 		return 0;
226*eaf01ee5SSarah Walker 
227*eaf01ee5SSarah Walker 	case DRM_PVR_CTX_TYPE_COMPUTE:
228*eaf01ee5SSarah Walker 		ctx->queues.compute = pvr_queue_create(ctx, DRM_PVR_JOB_TYPE_COMPUTE,
229*eaf01ee5SSarah Walker 						       args, fw_ctx_map);
230*eaf01ee5SSarah Walker 		if (IS_ERR(ctx->queues.compute)) {
231*eaf01ee5SSarah Walker 			err = PTR_ERR(ctx->queues.compute);
232*eaf01ee5SSarah Walker 			ctx->queues.compute = NULL;
233*eaf01ee5SSarah Walker 			goto err_destroy_queues;
234*eaf01ee5SSarah Walker 		}
235*eaf01ee5SSarah Walker 		return 0;
236*eaf01ee5SSarah Walker 
237*eaf01ee5SSarah Walker 	case DRM_PVR_CTX_TYPE_TRANSFER_FRAG:
238*eaf01ee5SSarah Walker 		ctx->queues.transfer = pvr_queue_create(ctx, DRM_PVR_JOB_TYPE_TRANSFER_FRAG,
239*eaf01ee5SSarah Walker 							args, fw_ctx_map);
240*eaf01ee5SSarah Walker 		if (IS_ERR(ctx->queues.transfer)) {
241*eaf01ee5SSarah Walker 			err = PTR_ERR(ctx->queues.transfer);
242*eaf01ee5SSarah Walker 			ctx->queues.transfer = NULL;
243*eaf01ee5SSarah Walker 			goto err_destroy_queues;
244*eaf01ee5SSarah Walker 		}
245*eaf01ee5SSarah Walker 		return 0;
246*eaf01ee5SSarah Walker 	}
247*eaf01ee5SSarah Walker 
248*eaf01ee5SSarah Walker 	return -EINVAL;
249*eaf01ee5SSarah Walker 
250*eaf01ee5SSarah Walker err_destroy_queues:
251*eaf01ee5SSarah Walker 	pvr_context_destroy_queues(ctx);
252*eaf01ee5SSarah Walker 	return err;
253*eaf01ee5SSarah Walker }
254*eaf01ee5SSarah Walker 
255*eaf01ee5SSarah Walker /**
256*eaf01ee5SSarah Walker  * pvr_context_kill_queues() - Kill queues attached to context.
257*eaf01ee5SSarah Walker  * @ctx: Context to kill queues on.
258*eaf01ee5SSarah Walker  *
259*eaf01ee5SSarah Walker  * Killing the queues implies making them unusable for future jobs, while still
260*eaf01ee5SSarah Walker  * letting the currently submitted jobs a chance to finish. Queue resources will
261*eaf01ee5SSarah Walker  * stay around until pvr_context_destroy_queues() is called.
262*eaf01ee5SSarah Walker  */
263*eaf01ee5SSarah Walker static void pvr_context_kill_queues(struct pvr_context *ctx)
264*eaf01ee5SSarah Walker {
265*eaf01ee5SSarah Walker 	switch (ctx->type) {
266*eaf01ee5SSarah Walker 	case DRM_PVR_CTX_TYPE_RENDER:
267*eaf01ee5SSarah Walker 		pvr_queue_kill(ctx->queues.fragment);
268*eaf01ee5SSarah Walker 		pvr_queue_kill(ctx->queues.geometry);
269*eaf01ee5SSarah Walker 		break;
270*eaf01ee5SSarah Walker 	case DRM_PVR_CTX_TYPE_COMPUTE:
271*eaf01ee5SSarah Walker 		pvr_queue_kill(ctx->queues.compute);
272*eaf01ee5SSarah Walker 		break;
273*eaf01ee5SSarah Walker 	case DRM_PVR_CTX_TYPE_TRANSFER_FRAG:
274*eaf01ee5SSarah Walker 		pvr_queue_kill(ctx->queues.transfer);
275*eaf01ee5SSarah Walker 		break;
276*eaf01ee5SSarah Walker 	}
277*eaf01ee5SSarah Walker }
278*eaf01ee5SSarah Walker 
279*eaf01ee5SSarah Walker /**
280d2d79d29SSarah Walker  * pvr_context_create() - Create a context.
281d2d79d29SSarah Walker  * @pvr_file: File to attach the created context to.
282d2d79d29SSarah Walker  * @args: Context creation arguments.
283d2d79d29SSarah Walker  *
284d2d79d29SSarah Walker  * Return:
285d2d79d29SSarah Walker  *  * 0 on success, or
286d2d79d29SSarah Walker  *  * A negative error code on failure.
287d2d79d29SSarah Walker  */
288d2d79d29SSarah Walker int pvr_context_create(struct pvr_file *pvr_file, struct drm_pvr_ioctl_create_context_args *args)
289d2d79d29SSarah Walker {
290d2d79d29SSarah Walker 	struct pvr_device *pvr_dev = pvr_file->pvr_dev;
291d2d79d29SSarah Walker 	struct pvr_context *ctx;
292d2d79d29SSarah Walker 	int ctx_size;
293d2d79d29SSarah Walker 	int err;
294d2d79d29SSarah Walker 
295d2d79d29SSarah Walker 	/* Context creation flags are currently unused and must be zero. */
296d2d79d29SSarah Walker 	if (args->flags)
297d2d79d29SSarah Walker 		return -EINVAL;
298d2d79d29SSarah Walker 
299d2d79d29SSarah Walker 	ctx_size = get_fw_obj_size(args->type);
300d2d79d29SSarah Walker 	if (ctx_size < 0)
301d2d79d29SSarah Walker 		return ctx_size;
302d2d79d29SSarah Walker 
303d2d79d29SSarah Walker 	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
304d2d79d29SSarah Walker 	if (!ctx)
305d2d79d29SSarah Walker 		return -ENOMEM;
306d2d79d29SSarah Walker 
307d2d79d29SSarah Walker 	ctx->data_size = ctx_size;
308d2d79d29SSarah Walker 	ctx->type = args->type;
309d2d79d29SSarah Walker 	ctx->flags = args->flags;
310d2d79d29SSarah Walker 	ctx->pvr_dev = pvr_dev;
311d2d79d29SSarah Walker 	kref_init(&ctx->ref_count);
312d2d79d29SSarah Walker 
313d2d79d29SSarah Walker 	err = remap_priority(pvr_file, args->priority, &ctx->priority);
314d2d79d29SSarah Walker 	if (err)
315d2d79d29SSarah Walker 		goto err_free_ctx;
316d2d79d29SSarah Walker 
317d2d79d29SSarah Walker 	ctx->vm_ctx = pvr_vm_context_lookup(pvr_file, args->vm_context_handle);
318d2d79d29SSarah Walker 	if (IS_ERR(ctx->vm_ctx)) {
319d2d79d29SSarah Walker 		err = PTR_ERR(ctx->vm_ctx);
320d2d79d29SSarah Walker 		goto err_free_ctx;
321d2d79d29SSarah Walker 	}
322d2d79d29SSarah Walker 
323d2d79d29SSarah Walker 	ctx->data = kzalloc(ctx_size, GFP_KERNEL);
324d2d79d29SSarah Walker 	if (!ctx->data) {
325d2d79d29SSarah Walker 		err = -ENOMEM;
326d2d79d29SSarah Walker 		goto err_put_vm;
327d2d79d29SSarah Walker 	}
328d2d79d29SSarah Walker 
329*eaf01ee5SSarah Walker 	err = pvr_context_create_queues(ctx, args, ctx->data);
330d2d79d29SSarah Walker 	if (err)
331d2d79d29SSarah Walker 		goto err_free_ctx_data;
332d2d79d29SSarah Walker 
333*eaf01ee5SSarah Walker 	err = init_fw_objs(ctx, args, ctx->data);
334*eaf01ee5SSarah Walker 	if (err)
335*eaf01ee5SSarah Walker 		goto err_destroy_queues;
336*eaf01ee5SSarah Walker 
337d2d79d29SSarah Walker 	err = pvr_fw_object_create(pvr_dev, ctx_size, PVR_BO_FW_FLAGS_DEVICE_UNCACHED,
338d2d79d29SSarah Walker 				   ctx_fw_data_init, ctx, &ctx->fw_obj);
339d2d79d29SSarah Walker 	if (err)
340d2d79d29SSarah Walker 		goto err_free_ctx_data;
341d2d79d29SSarah Walker 
342d2d79d29SSarah Walker 	err = xa_alloc(&pvr_dev->ctx_ids, &ctx->ctx_id, ctx, xa_limit_32b, GFP_KERNEL);
343d2d79d29SSarah Walker 	if (err)
344d2d79d29SSarah Walker 		goto err_destroy_fw_obj;
345d2d79d29SSarah Walker 
346d2d79d29SSarah Walker 	err = xa_alloc(&pvr_file->ctx_handles, &args->handle, ctx, xa_limit_32b, GFP_KERNEL);
347d2d79d29SSarah Walker 	if (err) {
348d2d79d29SSarah Walker 		/*
349d2d79d29SSarah Walker 		 * It's possible that another thread could have taken a reference on the context at
350d2d79d29SSarah Walker 		 * this point as it is in the ctx_ids xarray. Therefore instead of directly
351d2d79d29SSarah Walker 		 * destroying the context, drop a reference instead.
352d2d79d29SSarah Walker 		 */
353d2d79d29SSarah Walker 		pvr_context_put(ctx);
354d2d79d29SSarah Walker 		return err;
355d2d79d29SSarah Walker 	}
356d2d79d29SSarah Walker 
357d2d79d29SSarah Walker 	return 0;
358d2d79d29SSarah Walker 
359d2d79d29SSarah Walker err_destroy_fw_obj:
360d2d79d29SSarah Walker 	pvr_fw_object_destroy(ctx->fw_obj);
361d2d79d29SSarah Walker 
362*eaf01ee5SSarah Walker err_destroy_queues:
363*eaf01ee5SSarah Walker 	pvr_context_destroy_queues(ctx);
364*eaf01ee5SSarah Walker 
365d2d79d29SSarah Walker err_free_ctx_data:
366d2d79d29SSarah Walker 	kfree(ctx->data);
367d2d79d29SSarah Walker 
368d2d79d29SSarah Walker err_put_vm:
369d2d79d29SSarah Walker 	pvr_vm_context_put(ctx->vm_ctx);
370d2d79d29SSarah Walker 
371d2d79d29SSarah Walker err_free_ctx:
372d2d79d29SSarah Walker 	kfree(ctx);
373d2d79d29SSarah Walker 	return err;
374d2d79d29SSarah Walker }
375d2d79d29SSarah Walker 
376d2d79d29SSarah Walker static void
377d2d79d29SSarah Walker pvr_context_release(struct kref *ref_count)
378d2d79d29SSarah Walker {
379d2d79d29SSarah Walker 	struct pvr_context *ctx =
380d2d79d29SSarah Walker 		container_of(ref_count, struct pvr_context, ref_count);
381d2d79d29SSarah Walker 	struct pvr_device *pvr_dev = ctx->pvr_dev;
382d2d79d29SSarah Walker 
383d2d79d29SSarah Walker 	xa_erase(&pvr_dev->ctx_ids, ctx->ctx_id);
384*eaf01ee5SSarah Walker 	pvr_context_destroy_queues(ctx);
385d2d79d29SSarah Walker 	pvr_fw_object_destroy(ctx->fw_obj);
386d2d79d29SSarah Walker 	kfree(ctx->data);
387d2d79d29SSarah Walker 	pvr_vm_context_put(ctx->vm_ctx);
388d2d79d29SSarah Walker 	kfree(ctx);
389d2d79d29SSarah Walker }
390d2d79d29SSarah Walker 
391d2d79d29SSarah Walker /**
392d2d79d29SSarah Walker  * pvr_context_put() - Release reference on context
393d2d79d29SSarah Walker  * @ctx: Target context.
394d2d79d29SSarah Walker  */
395d2d79d29SSarah Walker void
396d2d79d29SSarah Walker pvr_context_put(struct pvr_context *ctx)
397d2d79d29SSarah Walker {
398d2d79d29SSarah Walker 	if (ctx)
399d2d79d29SSarah Walker 		kref_put(&ctx->ref_count, pvr_context_release);
400d2d79d29SSarah Walker }
401d2d79d29SSarah Walker 
402d2d79d29SSarah Walker /**
403d2d79d29SSarah Walker  * pvr_context_destroy() - Destroy context
404d2d79d29SSarah Walker  * @pvr_file: Pointer to pvr_file structure.
405d2d79d29SSarah Walker  * @handle: Userspace context handle.
406d2d79d29SSarah Walker  *
407d2d79d29SSarah Walker  * Removes context from context list and drops initial reference. Context will
408d2d79d29SSarah Walker  * then be destroyed once all outstanding references are dropped.
409d2d79d29SSarah Walker  *
410d2d79d29SSarah Walker  * Return:
411d2d79d29SSarah Walker  *  * 0 on success, or
412d2d79d29SSarah Walker  *  * -%EINVAL if context not in context list.
413d2d79d29SSarah Walker  */
414d2d79d29SSarah Walker int
415d2d79d29SSarah Walker pvr_context_destroy(struct pvr_file *pvr_file, u32 handle)
416d2d79d29SSarah Walker {
417d2d79d29SSarah Walker 	struct pvr_context *ctx = xa_erase(&pvr_file->ctx_handles, handle);
418d2d79d29SSarah Walker 
419d2d79d29SSarah Walker 	if (!ctx)
420d2d79d29SSarah Walker 		return -EINVAL;
421d2d79d29SSarah Walker 
422*eaf01ee5SSarah Walker 	/* Make sure nothing can be queued to the queues after that point. */
423*eaf01ee5SSarah Walker 	pvr_context_kill_queues(ctx);
424*eaf01ee5SSarah Walker 
425d2d79d29SSarah Walker 	/* Release the reference held by the handle set. */
426d2d79d29SSarah Walker 	pvr_context_put(ctx);
427d2d79d29SSarah Walker 
428d2d79d29SSarah Walker 	return 0;
429d2d79d29SSarah Walker }
430d2d79d29SSarah Walker 
431d2d79d29SSarah Walker /**
432d2d79d29SSarah Walker  * pvr_destroy_contexts_for_file: Destroy any contexts associated with the given file
433d2d79d29SSarah Walker  * @pvr_file: Pointer to pvr_file structure.
434d2d79d29SSarah Walker  *
435d2d79d29SSarah Walker  * Removes all contexts associated with @pvr_file from the device context list and drops initial
436d2d79d29SSarah Walker  * references. Contexts will then be destroyed once all outstanding references are dropped.
437d2d79d29SSarah Walker  */
438d2d79d29SSarah Walker void pvr_destroy_contexts_for_file(struct pvr_file *pvr_file)
439d2d79d29SSarah Walker {
440d2d79d29SSarah Walker 	struct pvr_context *ctx;
441d2d79d29SSarah Walker 	unsigned long handle;
442d2d79d29SSarah Walker 
443d2d79d29SSarah Walker 	xa_for_each(&pvr_file->ctx_handles, handle, ctx)
444d2d79d29SSarah Walker 		pvr_context_destroy(pvr_file, handle);
445d2d79d29SSarah Walker }
446d2d79d29SSarah Walker 
447d2d79d29SSarah Walker /**
448d2d79d29SSarah Walker  * pvr_context_device_init() - Device level initialization for queue related resources.
449d2d79d29SSarah Walker  * @pvr_dev: The device to initialize.
450d2d79d29SSarah Walker  */
451d2d79d29SSarah Walker void pvr_context_device_init(struct pvr_device *pvr_dev)
452d2d79d29SSarah Walker {
453d2d79d29SSarah Walker 	xa_init_flags(&pvr_dev->ctx_ids, XA_FLAGS_ALLOC1);
454d2d79d29SSarah Walker }
455d2d79d29SSarah Walker 
456d2d79d29SSarah Walker /**
457d2d79d29SSarah Walker  * pvr_context_device_fini() - Device level cleanup for queue related resources.
458d2d79d29SSarah Walker  * @pvr_dev: The device to cleanup.
459d2d79d29SSarah Walker  */
460d2d79d29SSarah Walker void pvr_context_device_fini(struct pvr_device *pvr_dev)
461d2d79d29SSarah Walker {
462d2d79d29SSarah Walker 	WARN_ON(!xa_empty(&pvr_dev->ctx_ids));
463d2d79d29SSarah Walker 	xa_destroy(&pvr_dev->ctx_ids);
464d2d79d29SSarah Walker }
465