cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

nouveau_abi16.c (16471B)


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