256 lines · c
1/*2 * Copyright 2021 Red Hat Inc.3 *4 * Permission is hereby granted, free of charge, to any person obtaining a5 * copy of this software and associated documentation files (the "Software"),6 * to deal in the Software without restriction, including without limitation7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,8 * and/or sell copies of the Software, and to permit persons to whom the9 * Software is furnished to do so, subject to the following conditions:10 *11 * The above copyright notice and this permission notice shall be included in12 * all copies or substantial portions of the Software.13 *14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR20 * OTHER DEALINGS IN THE SOFTWARE.21 */22#include "cgrp.h"23#include "chan.h"24#include "chid.h"25#include "runl.h"26#include "priv.h"27 28#include <core/gpuobj.h>29#include <subdev/mmu.h>30 31static void32nvkm_cgrp_ectx_put(struct nvkm_cgrp *cgrp, struct nvkm_ectx **pectx)33{34 struct nvkm_ectx *ectx = *pectx;35 36 if (ectx) {37 struct nvkm_engn *engn = ectx->engn;38 39 if (refcount_dec_and_test(&ectx->refs)) {40 CGRP_TRACE(cgrp, "dtor ectx %d[%s]", engn->id, engn->engine->subdev.name);41 nvkm_object_del(&ectx->object);42 list_del(&ectx->head);43 kfree(ectx);44 }45 46 *pectx = NULL;47 }48}49 50static int51nvkm_cgrp_ectx_get(struct nvkm_cgrp *cgrp, struct nvkm_engn *engn, struct nvkm_ectx **pectx,52 struct nvkm_chan *chan, struct nvkm_client *client)53{54 struct nvkm_engine *engine = engn->engine;55 struct nvkm_oclass cclass = {56 .client = client,57 .engine = engine,58 };59 struct nvkm_ectx *ectx;60 int ret = 0;61 62 /* Look for an existing context for this engine in the channel group. */63 ectx = nvkm_list_find(ectx, &cgrp->ectxs, head, ectx->engn == engn);64 if (ectx) {65 refcount_inc(&ectx->refs);66 *pectx = ectx;67 return 0;68 }69 70 /* Nope - create a fresh one. */71 CGRP_TRACE(cgrp, "ctor ectx %d[%s]", engn->id, engn->engine->subdev.name);72 if (!(ectx = *pectx = kzalloc(sizeof(*ectx), GFP_KERNEL)))73 return -ENOMEM;74 75 ectx->engn = engn;76 refcount_set(&ectx->refs, 1);77 refcount_set(&ectx->uses, 0);78 list_add_tail(&ectx->head, &cgrp->ectxs);79 80 /* Allocate the HW structures. */81 if (engine->func->fifo.cclass)82 ret = engine->func->fifo.cclass(chan, &cclass, &ectx->object);83 else if (engine->func->cclass)84 ret = nvkm_object_new_(engine->func->cclass, &cclass, NULL, 0, &ectx->object);85 86 if (ret)87 nvkm_cgrp_ectx_put(cgrp, pectx);88 89 return ret;90}91 92void93nvkm_cgrp_vctx_put(struct nvkm_cgrp *cgrp, struct nvkm_vctx **pvctx)94{95 struct nvkm_vctx *vctx = *pvctx;96 97 if (vctx) {98 struct nvkm_engn *engn = vctx->ectx->engn;99 100 if (refcount_dec_and_test(&vctx->refs)) {101 CGRP_TRACE(cgrp, "dtor vctx %d[%s]", engn->id, engn->engine->subdev.name);102 nvkm_vmm_put(vctx->vmm, &vctx->vma);103 nvkm_gpuobj_del(&vctx->inst);104 105 nvkm_cgrp_ectx_put(cgrp, &vctx->ectx);106 if (vctx->vmm) {107 atomic_dec(&vctx->vmm->engref[engn->engine->subdev.type]);108 nvkm_vmm_unref(&vctx->vmm);109 }110 list_del(&vctx->head);111 kfree(vctx);112 }113 114 *pvctx = NULL;115 }116}117 118int119nvkm_cgrp_vctx_get(struct nvkm_cgrp *cgrp, struct nvkm_engn *engn, struct nvkm_chan *chan,120 struct nvkm_vctx **pvctx, struct nvkm_client *client)121{122 struct nvkm_ectx *ectx;123 struct nvkm_vctx *vctx;124 int ret;125 126 /* Look for an existing sub-context for this engine+VEID in the channel group. */127 vctx = nvkm_list_find(vctx, &cgrp->vctxs, head,128 vctx->ectx->engn == engn && vctx->vmm == chan->vmm);129 if (vctx) {130 refcount_inc(&vctx->refs);131 *pvctx = vctx;132 return 0;133 }134 135 /* Nope - create a fresh one. But, context first. */136 ret = nvkm_cgrp_ectx_get(cgrp, engn, &ectx, chan, client);137 if (ret) {138 CGRP_ERROR(cgrp, "ectx %d[%s]: %d", engn->id, engn->engine->subdev.name, ret);139 return ret;140 }141 142 /* Now, create the sub-context. */143 CGRP_TRACE(cgrp, "ctor vctx %d[%s]", engn->id, engn->engine->subdev.name);144 if (!(vctx = *pvctx = kzalloc(sizeof(*vctx), GFP_KERNEL))) {145 nvkm_cgrp_ectx_put(cgrp, &ectx);146 return -ENOMEM;147 }148 149 vctx->ectx = ectx;150 vctx->vmm = nvkm_vmm_ref(chan->vmm);151 refcount_set(&vctx->refs, 1);152 list_add_tail(&vctx->head, &cgrp->vctxs);153 154 /* MMU on some GPUs needs to know engine usage for TLB invalidation. */155 if (vctx->vmm)156 atomic_inc(&vctx->vmm->engref[engn->engine->subdev.type]);157 158 /* Allocate the HW structures. */159 if (engn->func->ctor2) {160 ret = engn->func->ctor2(engn, vctx, chan);161 } else162 if (engn->func->bind) {163 ret = nvkm_object_bind(vctx->ectx->object, NULL, 0, &vctx->inst);164 if (ret == 0 && engn->func->ctor)165 ret = engn->func->ctor(engn, vctx);166 }167 168 if (ret)169 nvkm_cgrp_vctx_put(cgrp, pvctx);170 171 return ret;172}173 174static void175nvkm_cgrp_del(struct kref *kref)176{177 struct nvkm_cgrp *cgrp = container_of(kref, typeof(*cgrp), kref);178 struct nvkm_runl *runl = cgrp->runl;179 180 if (runl->cgid)181 nvkm_chid_put(runl->cgid, cgrp->id, &cgrp->lock);182 183 mutex_destroy(&cgrp->mutex);184 nvkm_vmm_unref(&cgrp->vmm);185 kfree(cgrp);186}187 188void189nvkm_cgrp_unref(struct nvkm_cgrp **pcgrp)190{191 struct nvkm_cgrp *cgrp = *pcgrp;192 193 if (!cgrp)194 return;195 196 kref_put(&cgrp->kref, nvkm_cgrp_del);197 *pcgrp = NULL;198}199 200struct nvkm_cgrp *201nvkm_cgrp_ref(struct nvkm_cgrp *cgrp)202{203 if (cgrp)204 kref_get(&cgrp->kref);205 206 return cgrp;207}208 209void210nvkm_cgrp_put(struct nvkm_cgrp **pcgrp, unsigned long irqflags)211{212 struct nvkm_cgrp *cgrp = *pcgrp;213 214 if (!cgrp)215 return;216 217 *pcgrp = NULL;218 spin_unlock_irqrestore(&cgrp->lock, irqflags);219}220 221int222nvkm_cgrp_new(struct nvkm_runl *runl, const char *name, struct nvkm_vmm *vmm, bool hw,223 struct nvkm_cgrp **pcgrp)224{225 struct nvkm_cgrp *cgrp;226 227 if (!(cgrp = *pcgrp = kmalloc(sizeof(*cgrp), GFP_KERNEL)))228 return -ENOMEM;229 230 cgrp->func = runl->fifo->func->cgrp.func;231 strscpy(cgrp->name, name, sizeof(cgrp->name));232 cgrp->runl = runl;233 cgrp->vmm = nvkm_vmm_ref(vmm);234 cgrp->hw = hw;235 cgrp->id = -1;236 kref_init(&cgrp->kref);237 INIT_LIST_HEAD(&cgrp->chans);238 cgrp->chan_nr = 0;239 spin_lock_init(&cgrp->lock);240 INIT_LIST_HEAD(&cgrp->ectxs);241 INIT_LIST_HEAD(&cgrp->vctxs);242 mutex_init(&cgrp->mutex);243 atomic_set(&cgrp->rc, NVKM_CGRP_RC_NONE);244 245 if (runl->cgid) {246 cgrp->id = nvkm_chid_get(runl->cgid, cgrp);247 if (cgrp->id < 0) {248 RUNL_ERROR(runl, "!cgids");249 nvkm_cgrp_unref(pcgrp);250 return -ENOSPC;251 }252 }253 254 return 0;255}256