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