xref: /openbmc/linux/drivers/gpu/drm/nouveau/nvkm/engine/fifo/cgrp.c (revision f48dd2936138882d7755cbbc5d9984015c75980c)
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