253 lines
6.4 KiB
C
253 lines
6.4 KiB
C
|
/*
|
||
|
* Copyright 2021 Red Hat Inc.
|
||
|
*
|
||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||
|
* copy of this software and associated documentation files (the "Software"),
|
||
|
* to deal in the Software without restriction, including without limitation
|
||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||
|
* Software is furnished to do so, subject to the following conditions:
|
||
|
*
|
||
|
* The above copyright notice and this permission notice shall be included in
|
||
|
* all copies or substantial portions of the Software.
|
||
|
*
|
||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||
|
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||
|
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||
|
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||
|
* OTHER DEALINGS IN THE SOFTWARE.
|
||
|
*/
|
||
|
#include "cgrp.h"
|
||
|
#include "chan.h"
|
||
|
#include "chid.h"
|
||
|
#include "runl.h"
|
||
|
#include "priv.h"
|
||
|
|
||
|
#include <core/gpuobj.h>
|
||
|
#include <subdev/mmu.h>
|
||
|
|
||
|
static void
|
||
|
nvkm_cgrp_ectx_put(struct nvkm_cgrp *cgrp, struct nvkm_ectx **pectx)
|
||
|
{
|
||
|
struct nvkm_ectx *ectx = *pectx;
|
||
|
|
||
|
if (ectx) {
|
||
|
struct nvkm_engn *engn = ectx->engn;
|
||
|
|
||
|
if (refcount_dec_and_test(&ectx->refs)) {
|
||
|
CGRP_TRACE(cgrp, "dtor ectx %d[%s]", engn->id, engn->engine->subdev.name);
|
||
|
nvkm_object_del(&ectx->object);
|
||
|
list_del(&ectx->head);
|
||
|
kfree(ectx);
|
||
|
}
|
||
|
|
||
|
*pectx = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
nvkm_cgrp_ectx_get(struct nvkm_cgrp *cgrp, struct nvkm_engn *engn, struct nvkm_ectx **pectx,
|
||
|
struct nvkm_chan *chan, struct nvkm_client *client)
|
||
|
{
|
||
|
struct nvkm_engine *engine = engn->engine;
|
||
|
struct nvkm_oclass cclass = {
|
||
|
.client = client,
|
||
|
.engine = engine,
|
||
|
};
|
||
|
struct nvkm_ectx *ectx;
|
||
|
int ret = 0;
|
||
|
|
||
|
/* Look for an existing context for this engine in the channel group. */
|
||
|
ectx = nvkm_list_find(ectx, &cgrp->ectxs, head, ectx->engn == engn);
|
||
|
if (ectx) {
|
||
|
refcount_inc(&ectx->refs);
|
||
|
*pectx = ectx;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* Nope - create a fresh one. */
|
||
|
CGRP_TRACE(cgrp, "ctor ectx %d[%s]", engn->id, engn->engine->subdev.name);
|
||
|
if (!(ectx = *pectx = kzalloc(sizeof(*ectx), GFP_KERNEL)))
|
||
|
return -ENOMEM;
|
||
|
|
||
|
ectx->engn = engn;
|
||
|
refcount_set(&ectx->refs, 1);
|
||
|
refcount_set(&ectx->uses, 0);
|
||
|
list_add_tail(&ectx->head, &cgrp->ectxs);
|
||
|
|
||
|
/* Allocate the HW structures. */
|
||
|
if (engine->func->fifo.cclass)
|
||
|
ret = engine->func->fifo.cclass(chan, &cclass, &ectx->object);
|
||
|
else if (engine->func->cclass)
|
||
|
ret = nvkm_object_new_(engine->func->cclass, &cclass, NULL, 0, &ectx->object);
|
||
|
|
||
|
if (ret)
|
||
|
nvkm_cgrp_ectx_put(cgrp, pectx);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
nvkm_cgrp_vctx_put(struct nvkm_cgrp *cgrp, struct nvkm_vctx **pvctx)
|
||
|
{
|
||
|
struct nvkm_vctx *vctx = *pvctx;
|
||
|
|
||
|
if (vctx) {
|
||
|
struct nvkm_engn *engn = vctx->ectx->engn;
|
||
|
|
||
|
if (refcount_dec_and_test(&vctx->refs)) {
|
||
|
CGRP_TRACE(cgrp, "dtor vctx %d[%s]", engn->id, engn->engine->subdev.name);
|
||
|
nvkm_vmm_put(vctx->vmm, &vctx->vma);
|
||
|
nvkm_gpuobj_del(&vctx->inst);
|
||
|
|
||
|
nvkm_cgrp_ectx_put(cgrp, &vctx->ectx);
|
||
|
if (vctx->vmm) {
|
||
|
atomic_dec(&vctx->vmm->engref[engn->engine->subdev.type]);
|
||
|
nvkm_vmm_unref(&vctx->vmm);
|
||
|
}
|
||
|
list_del(&vctx->head);
|
||
|
kfree(vctx);
|
||
|
}
|
||
|
|
||
|
*pvctx = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int
|
||
|
nvkm_cgrp_vctx_get(struct nvkm_cgrp *cgrp, struct nvkm_engn *engn, struct nvkm_chan *chan,
|
||
|
struct nvkm_vctx **pvctx, struct nvkm_client *client)
|
||
|
{
|
||
|
struct nvkm_ectx *ectx;
|
||
|
struct nvkm_vctx *vctx;
|
||
|
int ret;
|
||
|
|
||
|
/* Look for an existing sub-context for this engine+VEID in the channel group. */
|
||
|
vctx = nvkm_list_find(vctx, &cgrp->vctxs, head,
|
||
|
vctx->ectx->engn == engn && vctx->vmm == chan->vmm);
|
||
|
if (vctx) {
|
||
|
refcount_inc(&vctx->refs);
|
||
|
*pvctx = vctx;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* Nope - create a fresh one. But, context first. */
|
||
|
ret = nvkm_cgrp_ectx_get(cgrp, engn, &ectx, chan, client);
|
||
|
if (ret) {
|
||
|
CGRP_ERROR(cgrp, "ectx %d[%s]: %d", engn->id, engn->engine->subdev.name, ret);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/* Now, create the sub-context. */
|
||
|
CGRP_TRACE(cgrp, "ctor vctx %d[%s]", engn->id, engn->engine->subdev.name);
|
||
|
if (!(vctx = *pvctx = kzalloc(sizeof(*vctx), GFP_KERNEL))) {
|
||
|
nvkm_cgrp_ectx_put(cgrp, &ectx);
|
||
|
return -ENOMEM;
|
||
|
}
|
||
|
|
||
|
vctx->ectx = ectx;
|
||
|
vctx->vmm = nvkm_vmm_ref(chan->vmm);
|
||
|
refcount_set(&vctx->refs, 1);
|
||
|
list_add_tail(&vctx->head, &cgrp->vctxs);
|
||
|
|
||
|
/* MMU on some GPUs needs to know engine usage for TLB invalidation. */
|
||
|
if (vctx->vmm)
|
||
|
atomic_inc(&vctx->vmm->engref[engn->engine->subdev.type]);
|
||
|
|
||
|
/* Allocate the HW structures. */
|
||
|
if (engn->func->bind) {
|
||
|
ret = nvkm_object_bind(vctx->ectx->object, NULL, 0, &vctx->inst);
|
||
|
if (ret == 0 && engn->func->ctor)
|
||
|
ret = engn->func->ctor(engn, vctx);
|
||
|
}
|
||
|
|
||
|
if (ret)
|
||
|
nvkm_cgrp_vctx_put(cgrp, pvctx);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
nvkm_cgrp_del(struct kref *kref)
|
||
|
{
|
||
|
struct nvkm_cgrp *cgrp = container_of(kref, typeof(*cgrp), kref);
|
||
|
struct nvkm_runl *runl = cgrp->runl;
|
||
|
|
||
|
if (runl->cgid)
|
||
|
nvkm_chid_put(runl->cgid, cgrp->id, &cgrp->lock);
|
||
|
|
||
|
mutex_destroy(&cgrp->mutex);
|
||
|
nvkm_vmm_unref(&cgrp->vmm);
|
||
|
kfree(cgrp);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
nvkm_cgrp_unref(struct nvkm_cgrp **pcgrp)
|
||
|
{
|
||
|
struct nvkm_cgrp *cgrp = *pcgrp;
|
||
|
|
||
|
if (!cgrp)
|
||
|
return;
|
||
|
|
||
|
kref_put(&cgrp->kref, nvkm_cgrp_del);
|
||
|
*pcgrp = NULL;
|
||
|
}
|
||
|
|
||
|
struct nvkm_cgrp *
|
||
|
nvkm_cgrp_ref(struct nvkm_cgrp *cgrp)
|
||
|
{
|
||
|
if (cgrp)
|
||
|
kref_get(&cgrp->kref);
|
||
|
|
||
|
return cgrp;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
nvkm_cgrp_put(struct nvkm_cgrp **pcgrp, unsigned long irqflags)
|
||
|
{
|
||
|
struct nvkm_cgrp *cgrp = *pcgrp;
|
||
|
|
||
|
if (!cgrp)
|
||
|
return;
|
||
|
|
||
|
*pcgrp = NULL;
|
||
|
spin_unlock_irqrestore(&cgrp->lock, irqflags);
|
||
|
}
|
||
|
|
||
|
int
|
||
|
nvkm_cgrp_new(struct nvkm_runl *runl, const char *name, struct nvkm_vmm *vmm, bool hw,
|
||
|
struct nvkm_cgrp **pcgrp)
|
||
|
{
|
||
|
struct nvkm_cgrp *cgrp;
|
||
|
|
||
|
if (!(cgrp = *pcgrp = kmalloc(sizeof(*cgrp), GFP_KERNEL)))
|
||
|
return -ENOMEM;
|
||
|
|
||
|
cgrp->func = runl->fifo->func->cgrp.func;
|
||
|
strscpy(cgrp->name, name, sizeof(cgrp->name));
|
||
|
cgrp->runl = runl;
|
||
|
cgrp->vmm = nvkm_vmm_ref(vmm);
|
||
|
cgrp->hw = hw;
|
||
|
cgrp->id = -1;
|
||
|
kref_init(&cgrp->kref);
|
||
|
INIT_LIST_HEAD(&cgrp->chans);
|
||
|
cgrp->chan_nr = 0;
|
||
|
spin_lock_init(&cgrp->lock);
|
||
|
INIT_LIST_HEAD(&cgrp->ectxs);
|
||
|
INIT_LIST_HEAD(&cgrp->vctxs);
|
||
|
mutex_init(&cgrp->mutex);
|
||
|
atomic_set(&cgrp->rc, NVKM_CGRP_RC_NONE);
|
||
|
|
||
|
if (runl->cgid) {
|
||
|
cgrp->id = nvkm_chid_get(runl->cgid, cgrp);
|
||
|
if (cgrp->id < 0) {
|
||
|
RUNL_ERROR(runl, "!cgids");
|
||
|
nvkm_cgrp_unref(pcgrp);
|
||
|
return -ENOSPC;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|