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