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