1 /* 2 * Copyright 2021 Red Hat Inc. 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice shall be included in 12 * all copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 18 * 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 OR 20 * 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 <subdev/mmu.h> 29 30 static void 31 nvkm_cgrp_ectx_put(struct nvkm_cgrp *cgrp, struct nvkm_ectx **pectx) 32 { 33 struct nvkm_ectx *ectx = *pectx; 34 35 if (ectx) { 36 struct nvkm_engn *engn = ectx->engn; 37 38 if (refcount_dec_and_test(&ectx->refs)) { 39 CGRP_TRACE(cgrp, "dtor ectx %d[%s]", engn->id, engn->engine->subdev.name); 40 list_del(&ectx->head); 41 kfree(ectx); 42 } 43 44 *pectx = NULL; 45 } 46 } 47 48 static int 49 nvkm_cgrp_ectx_get(struct nvkm_cgrp *cgrp, struct nvkm_engn *engn, struct nvkm_ectx **pectx, 50 struct nvkm_chan *chan, struct nvkm_client *client) 51 { 52 struct nvkm_ectx *ectx; 53 int ret = 0; 54 55 /* Look for an existing context for this engine in the channel group. */ 56 ectx = nvkm_list_find(ectx, &cgrp->ectxs, head, ectx->engn == engn); 57 if (ectx) { 58 refcount_inc(&ectx->refs); 59 *pectx = ectx; 60 return 0; 61 } 62 63 /* Nope - create a fresh one. */ 64 CGRP_TRACE(cgrp, "ctor ectx %d[%s]", engn->id, engn->engine->subdev.name); 65 if (!(ectx = *pectx = kzalloc(sizeof(*ectx), GFP_KERNEL))) 66 return -ENOMEM; 67 68 ectx->engn = engn; 69 refcount_set(&ectx->refs, 1); 70 list_add_tail(&ectx->head, &cgrp->ectxs); 71 return ret; 72 } 73 74 void 75 nvkm_cgrp_vctx_put(struct nvkm_cgrp *cgrp, struct nvkm_vctx **pvctx) 76 { 77 struct nvkm_vctx *vctx = *pvctx; 78 79 if (vctx) { 80 struct nvkm_engn *engn = vctx->ectx->engn; 81 82 if (refcount_dec_and_test(&vctx->refs)) { 83 CGRP_TRACE(cgrp, "dtor vctx %d[%s]", engn->id, engn->engine->subdev.name); 84 85 nvkm_cgrp_ectx_put(cgrp, &vctx->ectx); 86 if (vctx->vmm) { 87 atomic_dec(&vctx->vmm->engref[engn->engine->subdev.type]); 88 nvkm_vmm_unref(&vctx->vmm); 89 } 90 list_del(&vctx->head); 91 kfree(vctx); 92 } 93 94 *pvctx = NULL; 95 } 96 } 97 98 int 99 nvkm_cgrp_vctx_get(struct nvkm_cgrp *cgrp, struct nvkm_engn *engn, struct nvkm_chan *chan, 100 struct nvkm_vctx **pvctx, struct nvkm_client *client) 101 { 102 struct nvkm_ectx *ectx; 103 struct nvkm_vctx *vctx; 104 int ret; 105 106 /* Look for an existing sub-context for this engine+VEID in the channel group. */ 107 vctx = nvkm_list_find(vctx, &cgrp->vctxs, head, 108 vctx->ectx->engn == engn && vctx->vmm == chan->vmm); 109 if (vctx) { 110 refcount_inc(&vctx->refs); 111 *pvctx = vctx; 112 return 0; 113 } 114 115 /* Nope - create a fresh one. But, context first. */ 116 ret = nvkm_cgrp_ectx_get(cgrp, engn, &ectx, chan, client); 117 if (ret) { 118 CGRP_ERROR(cgrp, "ectx %d[%s]: %d", engn->id, engn->engine->subdev.name, ret); 119 return ret; 120 } 121 122 /* Now, create the sub-context. */ 123 CGRP_TRACE(cgrp, "ctor vctx %d[%s]", engn->id, engn->engine->subdev.name); 124 if (!(vctx = *pvctx = kzalloc(sizeof(*vctx), GFP_KERNEL))) { 125 nvkm_cgrp_ectx_put(cgrp, &ectx); 126 return -ENOMEM; 127 } 128 129 vctx->ectx = ectx; 130 vctx->vmm = nvkm_vmm_ref(chan->vmm); 131 refcount_set(&vctx->refs, 1); 132 list_add_tail(&vctx->head, &cgrp->vctxs); 133 return ret; 134 } 135 136 static void 137 nvkm_cgrp_del(struct kref *kref) 138 { 139 struct nvkm_cgrp *cgrp = container_of(kref, typeof(*cgrp), kref); 140 struct nvkm_runl *runl = cgrp->runl; 141 142 if (runl->cgid) 143 nvkm_chid_put(runl->cgid, cgrp->id, &cgrp->lock); 144 145 mutex_destroy(&cgrp->mutex); 146 nvkm_vmm_unref(&cgrp->vmm); 147 kfree(cgrp); 148 } 149 150 void 151 nvkm_cgrp_unref(struct nvkm_cgrp **pcgrp) 152 { 153 struct nvkm_cgrp *cgrp = *pcgrp; 154 155 if (!cgrp) 156 return; 157 158 kref_put(&cgrp->kref, nvkm_cgrp_del); 159 *pcgrp = NULL; 160 } 161 162 struct nvkm_cgrp * 163 nvkm_cgrp_ref(struct nvkm_cgrp *cgrp) 164 { 165 if (cgrp) 166 kref_get(&cgrp->kref); 167 168 return cgrp; 169 } 170 171 int 172 nvkm_cgrp_new(struct nvkm_runl *runl, const char *name, struct nvkm_vmm *vmm, bool hw, 173 struct nvkm_cgrp **pcgrp) 174 { 175 struct nvkm_cgrp *cgrp; 176 177 if (!(cgrp = *pcgrp = kmalloc(sizeof(*cgrp), GFP_KERNEL))) 178 return -ENOMEM; 179 180 cgrp->func = runl->fifo->func->cgrp.func; 181 strscpy(cgrp->name, name, sizeof(cgrp->name)); 182 cgrp->runl = runl; 183 cgrp->vmm = nvkm_vmm_ref(vmm); 184 cgrp->hw = hw; 185 cgrp->id = -1; 186 kref_init(&cgrp->kref); 187 cgrp->chans = NULL; 188 cgrp->chan_nr = 0; 189 spin_lock_init(&cgrp->lock); 190 INIT_LIST_HEAD(&cgrp->ectxs); 191 INIT_LIST_HEAD(&cgrp->vctxs); 192 mutex_init(&cgrp->mutex); 193 194 if (runl->cgid) { 195 cgrp->id = nvkm_chid_get(runl->cgid, cgrp); 196 if (cgrp->id < 0) { 197 RUNL_ERROR(runl, "!cgids"); 198 nvkm_cgrp_unref(pcgrp); 199 return -ENOSPC; 200 } 201 } 202 203 return 0; 204 } 205