• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2012 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  */
23 
24 #include <nvif/client.h>
25 #include <nvif/driver.h>
26 #include <nvif/ioctl.h>
27 #include <nvif/class.h>
28 #include <nvif/unpack.h>
29 
30 #include "nouveau_drm.h"
31 #include "nouveau_dma.h"
32 #include "nouveau_gem.h"
33 #include "nouveau_chan.h"
34 #include "nouveau_abi16.h"
35 
36 static struct nouveau_abi16 *
nouveau_abi16(struct drm_file * file_priv)37 nouveau_abi16(struct drm_file *file_priv)
38 {
39 	struct nouveau_cli *cli = nouveau_cli(file_priv);
40 	if (!cli->abi16) {
41 		struct nouveau_abi16 *abi16;
42 		cli->abi16 = abi16 = kzalloc(sizeof(*abi16), GFP_KERNEL);
43 		if (cli->abi16) {
44 			struct nv_device_v0 args = {
45 				.device = ~0ULL,
46 			};
47 
48 			INIT_LIST_HEAD(&abi16->channels);
49 
50 			/* allocate device object targeting client's default
51 			 * device (ie. the one that belongs to the fd it
52 			 * opened)
53 			 */
54 			if (nvif_device_init(&cli->base.object, 0, NV_DEVICE,
55 					     &args, sizeof(args),
56 					     &abi16->device) == 0)
57 				return cli->abi16;
58 
59 			kfree(cli->abi16);
60 			cli->abi16 = NULL;
61 		}
62 	}
63 	return cli->abi16;
64 }
65 
66 struct nouveau_abi16 *
nouveau_abi16_get(struct drm_file * file_priv)67 nouveau_abi16_get(struct drm_file *file_priv)
68 {
69 	struct nouveau_cli *cli = nouveau_cli(file_priv);
70 	mutex_lock(&cli->mutex);
71 	if (nouveau_abi16(file_priv))
72 		return cli->abi16;
73 	mutex_unlock(&cli->mutex);
74 	return NULL;
75 }
76 
77 int
nouveau_abi16_put(struct nouveau_abi16 * abi16,int ret)78 nouveau_abi16_put(struct nouveau_abi16 *abi16, int ret)
79 {
80 	struct nouveau_cli *cli = (void *)abi16->device.object.client;
81 	mutex_unlock(&cli->mutex);
82 	return ret;
83 }
84 
85 s32
nouveau_abi16_swclass(struct nouveau_drm * drm)86 nouveau_abi16_swclass(struct nouveau_drm *drm)
87 {
88 	switch (drm->device.info.family) {
89 	case NV_DEVICE_INFO_V0_TNT:
90 		return NVIF_IOCTL_NEW_V0_SW_NV04;
91 	case NV_DEVICE_INFO_V0_CELSIUS:
92 	case NV_DEVICE_INFO_V0_KELVIN:
93 	case NV_DEVICE_INFO_V0_RANKINE:
94 	case NV_DEVICE_INFO_V0_CURIE:
95 		return NVIF_IOCTL_NEW_V0_SW_NV10;
96 	case NV_DEVICE_INFO_V0_TESLA:
97 		return NVIF_IOCTL_NEW_V0_SW_NV50;
98 	case NV_DEVICE_INFO_V0_FERMI:
99 	case NV_DEVICE_INFO_V0_KEPLER:
100 	case NV_DEVICE_INFO_V0_MAXWELL:
101 		return NVIF_IOCTL_NEW_V0_SW_GF100;
102 	}
103 
104 	return 0x0000;
105 }
106 
107 static void
nouveau_abi16_ntfy_fini(struct nouveau_abi16_chan * chan,struct nouveau_abi16_ntfy * ntfy)108 nouveau_abi16_ntfy_fini(struct nouveau_abi16_chan *chan,
109 			struct nouveau_abi16_ntfy *ntfy)
110 {
111 	nvif_object_fini(&ntfy->object);
112 	nvkm_mm_free(&chan->heap, &ntfy->node);
113 	list_del(&ntfy->head);
114 	kfree(ntfy);
115 }
116 
117 static void
nouveau_abi16_chan_fini(struct nouveau_abi16 * abi16,struct nouveau_abi16_chan * chan)118 nouveau_abi16_chan_fini(struct nouveau_abi16 *abi16,
119 			struct nouveau_abi16_chan *chan)
120 {
121 	struct nouveau_abi16_ntfy *ntfy, *temp;
122 
123 	/* wait for all activity to stop before releasing notify object, which
124 	 * may be still in use */
125 	if (chan->chan && chan->ntfy)
126 		nouveau_channel_idle(chan->chan);
127 
128 	/* cleanup notifier state */
129 	list_for_each_entry_safe(ntfy, temp, &chan->notifiers, head) {
130 		nouveau_abi16_ntfy_fini(chan, ntfy);
131 	}
132 
133 	if (chan->ntfy) {
134 		nouveau_bo_vma_del(chan->ntfy, &chan->ntfy_vma);
135 		nouveau_bo_unpin(chan->ntfy);
136 		drm_gem_object_unreference_unlocked(&chan->ntfy->gem);
137 	}
138 
139 	if (chan->heap.block_size)
140 		nvkm_mm_fini(&chan->heap);
141 
142 	/* destroy channel object, all children will be killed too */
143 	if (chan->chan) {
144 		nouveau_channel_idle(chan->chan);
145 		nouveau_channel_del(&chan->chan);
146 	}
147 
148 	list_del(&chan->head);
149 	kfree(chan);
150 }
151 
152 void
nouveau_abi16_fini(struct nouveau_abi16 * abi16)153 nouveau_abi16_fini(struct nouveau_abi16 *abi16)
154 {
155 	struct nouveau_cli *cli = (void *)abi16->device.object.client;
156 	struct nouveau_abi16_chan *chan, *temp;
157 
158 	/* cleanup channels */
159 	list_for_each_entry_safe(chan, temp, &abi16->channels, head) {
160 		nouveau_abi16_chan_fini(abi16, chan);
161 	}
162 
163 	/* destroy the device object */
164 	nvif_device_fini(&abi16->device);
165 
166 	kfree(cli->abi16);
167 	cli->abi16 = NULL;
168 }
169 
170 int
nouveau_abi16_ioctl_getparam(ABI16_IOCTL_ARGS)171 nouveau_abi16_ioctl_getparam(ABI16_IOCTL_ARGS)
172 {
173 	struct nouveau_cli *cli = nouveau_cli(file_priv);
174 	struct nouveau_drm *drm = nouveau_drm(dev);
175 	struct nvif_device *device = &drm->device;
176 	struct nvkm_gr *gr = nvxx_gr(device);
177 	struct drm_nouveau_getparam *getparam = data;
178 
179 	switch (getparam->param) {
180 	case NOUVEAU_GETPARAM_CHIPSET_ID:
181 		getparam->value = device->info.chipset;
182 		break;
183 	case NOUVEAU_GETPARAM_PCI_VENDOR:
184 		if (nvxx_device(device)->func->pci)
185 			getparam->value = dev->pdev->vendor;
186 		else
187 			getparam->value = 0;
188 		break;
189 	case NOUVEAU_GETPARAM_PCI_DEVICE:
190 		if (nvxx_device(device)->func->pci)
191 			getparam->value = dev->pdev->device;
192 		else
193 			getparam->value = 0;
194 		break;
195 	case NOUVEAU_GETPARAM_BUS_TYPE:
196 		if (!nvxx_device(device)->func->pci)
197 			getparam->value = 3;
198 		else
199 		if (drm_pci_device_is_agp(dev))
200 			getparam->value = 0;
201 		else
202 		if (!pci_is_pcie(dev->pdev))
203 			getparam->value = 1;
204 		else
205 			getparam->value = 2;
206 		break;
207 	case NOUVEAU_GETPARAM_FB_SIZE:
208 		getparam->value = drm->gem.vram_available;
209 		break;
210 	case NOUVEAU_GETPARAM_AGP_SIZE:
211 		getparam->value = drm->gem.gart_available;
212 		break;
213 	case NOUVEAU_GETPARAM_VM_VRAM_BASE:
214 		getparam->value = 0; /* deprecated */
215 		break;
216 	case NOUVEAU_GETPARAM_PTIMER_TIME:
217 		getparam->value = nvif_device_time(device);
218 		break;
219 	case NOUVEAU_GETPARAM_HAS_BO_USAGE:
220 		getparam->value = 1;
221 		break;
222 	case NOUVEAU_GETPARAM_HAS_PAGEFLIP:
223 		getparam->value = 1;
224 		break;
225 	case NOUVEAU_GETPARAM_GRAPH_UNITS:
226 		getparam->value = nvkm_gr_units(gr);
227 		break;
228 	default:
229 		NV_PRINTK(dbg, cli, "unknown parameter %lld\n", getparam->param);
230 		return -EINVAL;
231 	}
232 
233 	return 0;
234 }
235 
236 int
nouveau_abi16_ioctl_setparam(ABI16_IOCTL_ARGS)237 nouveau_abi16_ioctl_setparam(ABI16_IOCTL_ARGS)
238 {
239 	return -EINVAL;
240 }
241 
242 int
nouveau_abi16_ioctl_channel_alloc(ABI16_IOCTL_ARGS)243 nouveau_abi16_ioctl_channel_alloc(ABI16_IOCTL_ARGS)
244 {
245 	struct drm_nouveau_channel_alloc *init = data;
246 	struct nouveau_cli *cli = nouveau_cli(file_priv);
247 	struct nouveau_drm *drm = nouveau_drm(dev);
248 	struct nouveau_abi16 *abi16 = nouveau_abi16_get(file_priv);
249 	struct nouveau_abi16_chan *chan;
250 	struct nvif_device *device;
251 	int ret;
252 
253 	if (unlikely(!abi16))
254 		return -ENOMEM;
255 
256 	if (!drm->channel)
257 		return nouveau_abi16_put(abi16, -ENODEV);
258 
259 	device = &abi16->device;
260 
261 	/* hack to allow channel engine type specification on kepler */
262 	if (device->info.family >= NV_DEVICE_INFO_V0_KEPLER) {
263 		if (init->fb_ctxdma_handle != ~0)
264 			init->fb_ctxdma_handle = KEPLER_CHANNEL_GPFIFO_A_V0_ENGINE_GR;
265 		else
266 			init->fb_ctxdma_handle = init->tt_ctxdma_handle;
267 
268 		/* allow flips to be executed if this is a graphics channel */
269 		init->tt_ctxdma_handle = 0;
270 		if (init->fb_ctxdma_handle == KEPLER_CHANNEL_GPFIFO_A_V0_ENGINE_GR)
271 			init->tt_ctxdma_handle = 1;
272 	}
273 
274 	if (init->fb_ctxdma_handle == ~0 || init->tt_ctxdma_handle == ~0)
275 		return nouveau_abi16_put(abi16, -EINVAL);
276 
277 	/* allocate "abi16 channel" data and make up a handle for it */
278 	chan = kzalloc(sizeof(*chan), GFP_KERNEL);
279 	if (!chan)
280 		return nouveau_abi16_put(abi16, -ENOMEM);
281 
282 	INIT_LIST_HEAD(&chan->notifiers);
283 	list_add(&chan->head, &abi16->channels);
284 
285 	/* create channel object and initialise dma and fence management */
286 	ret = nouveau_channel_new(drm, device, init->fb_ctxdma_handle,
287 				  init->tt_ctxdma_handle, &chan->chan);
288 	if (ret)
289 		goto done;
290 
291 	init->channel = chan->chan->chid;
292 
293 	if (device->info.family >= NV_DEVICE_INFO_V0_TESLA)
294 		init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_VRAM |
295 					NOUVEAU_GEM_DOMAIN_GART;
296 	else
297 	if (chan->chan->push.buffer->bo.mem.mem_type == TTM_PL_VRAM)
298 		init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_VRAM;
299 	else
300 		init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_GART;
301 
302 	if (device->info.family < NV_DEVICE_INFO_V0_CELSIUS) {
303 		init->subchan[0].handle = 0x00000000;
304 		init->subchan[0].grclass = 0x0000;
305 		init->subchan[1].handle = chan->chan->nvsw.handle;
306 		init->subchan[1].grclass = 0x506e;
307 		init->nr_subchan = 2;
308 	}
309 
310 	/* Named memory object area */
311 	ret = nouveau_gem_new(dev, PAGE_SIZE, 0, NOUVEAU_GEM_DOMAIN_GART,
312 			      0, 0, &chan->ntfy);
313 	if (ret == 0)
314 		ret = nouveau_bo_pin(chan->ntfy, TTM_PL_FLAG_TT, false);
315 	if (ret)
316 		goto done;
317 
318 	if (device->info.family >= NV_DEVICE_INFO_V0_TESLA) {
319 		ret = nouveau_bo_vma_add(chan->ntfy, cli->vm,
320 					&chan->ntfy_vma);
321 		if (ret)
322 			goto done;
323 	}
324 
325 	ret = drm_gem_handle_create(file_priv, &chan->ntfy->gem,
326 				    &init->notifier_handle);
327 	if (ret)
328 		goto done;
329 
330 	ret = nvkm_mm_init(&chan->heap, 0, PAGE_SIZE, 1);
331 done:
332 	if (ret)
333 		nouveau_abi16_chan_fini(abi16, chan);
334 	return nouveau_abi16_put(abi16, ret);
335 }
336 
337 static struct nouveau_abi16_chan *
nouveau_abi16_chan(struct nouveau_abi16 * abi16,int channel)338 nouveau_abi16_chan(struct nouveau_abi16 *abi16, int channel)
339 {
340 	struct nouveau_abi16_chan *chan;
341 
342 	list_for_each_entry(chan, &abi16->channels, head) {
343 		if (chan->chan->chid == channel)
344 			return chan;
345 	}
346 
347 	return NULL;
348 }
349 
350 int
nouveau_abi16_usif(struct drm_file * file_priv,void * data,u32 size)351 nouveau_abi16_usif(struct drm_file *file_priv, void *data, u32 size)
352 {
353 	union {
354 		struct nvif_ioctl_v0 v0;
355 	} *args = data;
356 	struct nouveau_abi16_chan *chan;
357 	struct nouveau_abi16 *abi16;
358 	int ret;
359 
360 	if (nvif_unpack(args->v0, 0, 0, true)) {
361 		switch (args->v0.type) {
362 		case NVIF_IOCTL_V0_NEW:
363 		case NVIF_IOCTL_V0_MTHD:
364 		case NVIF_IOCTL_V0_SCLASS:
365 			break;
366 		default:
367 			return -EACCES;
368 		}
369 	} else
370 		return ret;
371 
372 	if (!(abi16 = nouveau_abi16(file_priv)))
373 		return -ENOMEM;
374 
375 	if (args->v0.token != ~0ULL) {
376 		if (!(chan = nouveau_abi16_chan(abi16, args->v0.token)))
377 			return -EINVAL;
378 		args->v0.object = nvif_handle(&chan->chan->user);
379 		args->v0.owner  = NVIF_IOCTL_V0_OWNER_ANY;
380 		return 0;
381 	}
382 
383 	args->v0.object = nvif_handle(&abi16->device.object);
384 	args->v0.owner  = NVIF_IOCTL_V0_OWNER_ANY;
385 	return 0;
386 }
387 
388 int
nouveau_abi16_ioctl_channel_free(ABI16_IOCTL_ARGS)389 nouveau_abi16_ioctl_channel_free(ABI16_IOCTL_ARGS)
390 {
391 	struct drm_nouveau_channel_free *req = data;
392 	struct nouveau_abi16 *abi16 = nouveau_abi16_get(file_priv);
393 	struct nouveau_abi16_chan *chan;
394 
395 	if (unlikely(!abi16))
396 		return -ENOMEM;
397 
398 	chan = nouveau_abi16_chan(abi16, req->channel);
399 	if (!chan)
400 		return nouveau_abi16_put(abi16, -ENOENT);
401 	nouveau_abi16_chan_fini(abi16, chan);
402 	return nouveau_abi16_put(abi16, 0);
403 }
404 
405 int
nouveau_abi16_ioctl_grobj_alloc(ABI16_IOCTL_ARGS)406 nouveau_abi16_ioctl_grobj_alloc(ABI16_IOCTL_ARGS)
407 {
408 	struct drm_nouveau_grobj_alloc *init = data;
409 	struct nouveau_abi16 *abi16 = nouveau_abi16_get(file_priv);
410 	struct nouveau_abi16_chan *chan;
411 	struct nouveau_abi16_ntfy *ntfy;
412 	struct nvif_client *client;
413 	struct nvif_sclass *sclass;
414 	s32 oclass = 0;
415 	int ret, i;
416 
417 	if (unlikely(!abi16))
418 		return -ENOMEM;
419 
420 	if (init->handle == ~0)
421 		return nouveau_abi16_put(abi16, -EINVAL);
422 	client = abi16->device.object.client;
423 
424 	chan = nouveau_abi16_chan(abi16, init->channel);
425 	if (!chan)
426 		return nouveau_abi16_put(abi16, -ENOENT);
427 
428 	ret = nvif_object_sclass_get(&chan->chan->user, &sclass);
429 	if (ret < 0)
430 		return nouveau_abi16_put(abi16, ret);
431 
432 	if ((init->class & 0x00ff) == 0x006e) {
433 		/* nvsw: compatibility with older 0x*6e class identifier */
434 		for (i = 0; !oclass && i < ret; i++) {
435 			switch (sclass[i].oclass) {
436 			case NVIF_IOCTL_NEW_V0_SW_NV04:
437 			case NVIF_IOCTL_NEW_V0_SW_NV10:
438 			case NVIF_IOCTL_NEW_V0_SW_NV50:
439 			case NVIF_IOCTL_NEW_V0_SW_GF100:
440 				oclass = sclass[i].oclass;
441 				break;
442 			default:
443 				break;
444 			}
445 		}
446 	} else
447 	if ((init->class & 0x00ff) == 0x00b1) {
448 		/* msvld: compatibility with incorrect version exposure */
449 		for (i = 0; i < ret; i++) {
450 			if ((sclass[i].oclass & 0x00ff) == 0x00b1) {
451 				oclass = sclass[i].oclass;
452 				break;
453 			}
454 		}
455 	} else
456 	if ((init->class & 0x00ff) == 0x00b2) { /* mspdec */
457 		/* mspdec: compatibility with incorrect version exposure */
458 		for (i = 0; i < ret; i++) {
459 			if ((sclass[i].oclass & 0x00ff) == 0x00b2) {
460 				oclass = sclass[i].oclass;
461 				break;
462 			}
463 		}
464 	} else
465 	if ((init->class & 0x00ff) == 0x00b3) { /* msppp */
466 		/* msppp: compatibility with incorrect version exposure */
467 		for (i = 0; i < ret; i++) {
468 			if ((sclass[i].oclass & 0x00ff) == 0x00b3) {
469 				oclass = sclass[i].oclass;
470 				break;
471 			}
472 		}
473 	} else {
474 		oclass = init->class;
475 	}
476 
477 	nvif_object_sclass_put(&sclass);
478 	if (!oclass)
479 		return nouveau_abi16_put(abi16, -EINVAL);
480 
481 	ntfy = kzalloc(sizeof(*ntfy), GFP_KERNEL);
482 	if (!ntfy)
483 		return nouveau_abi16_put(abi16, -ENOMEM);
484 
485 	list_add(&ntfy->head, &chan->notifiers);
486 
487 	client->route = NVDRM_OBJECT_ABI16;
488 	ret = nvif_object_init(&chan->chan->user, init->handle, oclass,
489 			       NULL, 0, &ntfy->object);
490 	client->route = NVDRM_OBJECT_NVIF;
491 
492 	if (ret)
493 		nouveau_abi16_ntfy_fini(chan, ntfy);
494 	return nouveau_abi16_put(abi16, ret);
495 }
496 
497 int
nouveau_abi16_ioctl_notifierobj_alloc(ABI16_IOCTL_ARGS)498 nouveau_abi16_ioctl_notifierobj_alloc(ABI16_IOCTL_ARGS)
499 {
500 	struct drm_nouveau_notifierobj_alloc *info = data;
501 	struct nouveau_drm *drm = nouveau_drm(dev);
502 	struct nouveau_abi16 *abi16 = nouveau_abi16_get(file_priv);
503 	struct nouveau_abi16_chan *chan;
504 	struct nouveau_abi16_ntfy *ntfy;
505 	struct nvif_device *device = &abi16->device;
506 	struct nvif_client *client;
507 	struct nv_dma_v0 args = {};
508 	int ret;
509 
510 	if (unlikely(!abi16))
511 		return -ENOMEM;
512 
513 	/* completely unnecessary for these chipsets... */
514 	if (unlikely(device->info.family >= NV_DEVICE_INFO_V0_FERMI))
515 		return nouveau_abi16_put(abi16, -EINVAL);
516 	client = abi16->device.object.client;
517 
518 	chan = nouveau_abi16_chan(abi16, info->channel);
519 	if (!chan)
520 		return nouveau_abi16_put(abi16, -ENOENT);
521 
522 	ntfy = kzalloc(sizeof(*ntfy), GFP_KERNEL);
523 	if (!ntfy)
524 		return nouveau_abi16_put(abi16, -ENOMEM);
525 
526 	list_add(&ntfy->head, &chan->notifiers);
527 
528 	ret = nvkm_mm_head(&chan->heap, 0, 1, info->size, info->size, 1,
529 			   &ntfy->node);
530 	if (ret)
531 		goto done;
532 
533 	args.start = ntfy->node->offset;
534 	args.limit = ntfy->node->offset + ntfy->node->length - 1;
535 	if (device->info.family >= NV_DEVICE_INFO_V0_TESLA) {
536 		args.target = NV_DMA_V0_TARGET_VM;
537 		args.access = NV_DMA_V0_ACCESS_VM;
538 		args.start += chan->ntfy_vma.offset;
539 		args.limit += chan->ntfy_vma.offset;
540 	} else
541 	if (drm->agp.bridge) {
542 		args.target = NV_DMA_V0_TARGET_AGP;
543 		args.access = NV_DMA_V0_ACCESS_RDWR;
544 		args.start += drm->agp.base + chan->ntfy->bo.offset;
545 		args.limit += drm->agp.base + chan->ntfy->bo.offset;
546 	} else {
547 		args.target = NV_DMA_V0_TARGET_VM;
548 		args.access = NV_DMA_V0_ACCESS_RDWR;
549 		args.start += chan->ntfy->bo.offset;
550 		args.limit += chan->ntfy->bo.offset;
551 	}
552 
553 	client->route = NVDRM_OBJECT_ABI16;
554 	client->super = true;
555 	ret = nvif_object_init(&chan->chan->user, info->handle,
556 			       NV_DMA_IN_MEMORY, &args, sizeof(args),
557 			       &ntfy->object);
558 	client->super = false;
559 	client->route = NVDRM_OBJECT_NVIF;
560 	if (ret)
561 		goto done;
562 
563 	info->offset = ntfy->node->offset;
564 done:
565 	if (ret)
566 		nouveau_abi16_ntfy_fini(chan, ntfy);
567 	return nouveau_abi16_put(abi16, ret);
568 }
569 
570 int
nouveau_abi16_ioctl_gpuobj_free(ABI16_IOCTL_ARGS)571 nouveau_abi16_ioctl_gpuobj_free(ABI16_IOCTL_ARGS)
572 {
573 	struct drm_nouveau_gpuobj_free *fini = data;
574 	struct nouveau_abi16 *abi16 = nouveau_abi16_get(file_priv);
575 	struct nouveau_abi16_chan *chan;
576 	struct nouveau_abi16_ntfy *ntfy;
577 	int ret = -ENOENT;
578 
579 	if (unlikely(!abi16))
580 		return -ENOMEM;
581 
582 	chan = nouveau_abi16_chan(abi16, fini->channel);
583 	if (!chan)
584 		return nouveau_abi16_put(abi16, -EINVAL);
585 
586 	/* synchronize with the user channel and destroy the gpu object */
587 	nouveau_channel_idle(chan->chan);
588 
589 	list_for_each_entry(ntfy, &chan->notifiers, head) {
590 		if (ntfy->object.handle == fini->handle) {
591 			nouveau_abi16_ntfy_fini(chan, ntfy);
592 			ret = 0;
593 			break;
594 		}
595 	}
596 
597 	return nouveau_abi16_put(abi16, ret);
598 }
599