1e88f27b3Smrg/* 2e88f27b3Smrg * Copyright 2012 Red Hat Inc. 3e88f27b3Smrg * 4e88f27b3Smrg * Permission is hereby granted, free of charge, to any person obtaining a 5e88f27b3Smrg * copy of this software and associated documentation files (the "Software"), 6e88f27b3Smrg * to deal in the Software without restriction, including without limitation 7e88f27b3Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8e88f27b3Smrg * and/or sell copies of the Software, and to permit persons to whom the 9e88f27b3Smrg * Software is furnished to do so, subject to the following conditions: 10e88f27b3Smrg * 11e88f27b3Smrg * The above copyright notice and this permission notice shall be included in 12e88f27b3Smrg * all copies or substantial portions of the Software. 13e88f27b3Smrg * 14e88f27b3Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15e88f27b3Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16e88f27b3Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17e88f27b3Smrg * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 18e88f27b3Smrg * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19e88f27b3Smrg * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 20e88f27b3Smrg * OTHER DEALINGS IN THE SOFTWARE. 21e88f27b3Smrg * 22e88f27b3Smrg * Authors: Ben Skeggs 23e88f27b3Smrg */ 24e88f27b3Smrg 25e88f27b3Smrg#include <stdio.h> 26e88f27b3Smrg#include <stdlib.h> 27e88f27b3Smrg#include <stdint.h> 28e88f27b3Smrg#include <stdbool.h> 29e88f27b3Smrg#include <string.h> 30e88f27b3Smrg#include <assert.h> 31e88f27b3Smrg#include <errno.h> 3249ef06a4Smrg#include <inttypes.h> 33e88f27b3Smrg 34e88f27b3Smrg#include <xf86drm.h> 35e88f27b3Smrg#include <xf86atomic.h> 36e88f27b3Smrg#include "libdrm_lists.h" 37e88f27b3Smrg#include "nouveau_drm.h" 38e88f27b3Smrg 39e88f27b3Smrg#include "nouveau.h" 40e88f27b3Smrg#include "private.h" 41e88f27b3Smrg 42e88f27b3Smrgstruct nouveau_pushbuf_krec { 43e88f27b3Smrg struct nouveau_pushbuf_krec *next; 44e88f27b3Smrg struct drm_nouveau_gem_pushbuf_bo buffer[NOUVEAU_GEM_MAX_BUFFERS]; 45e88f27b3Smrg struct drm_nouveau_gem_pushbuf_reloc reloc[NOUVEAU_GEM_MAX_RELOCS]; 46e88f27b3Smrg struct drm_nouveau_gem_pushbuf_push push[NOUVEAU_GEM_MAX_PUSH]; 47e88f27b3Smrg int nr_buffer; 48e88f27b3Smrg int nr_reloc; 49e88f27b3Smrg int nr_push; 50e88f27b3Smrg uint64_t vram_used; 51e88f27b3Smrg uint64_t gart_used; 52e88f27b3Smrg}; 53e88f27b3Smrg 54e88f27b3Smrgstruct nouveau_pushbuf_priv { 55e88f27b3Smrg struct nouveau_pushbuf base; 56e88f27b3Smrg struct nouveau_pushbuf_krec *list; 57e88f27b3Smrg struct nouveau_pushbuf_krec *krec; 58e88f27b3Smrg struct nouveau_list bctx_list; 59e88f27b3Smrg struct nouveau_bo *bo; 60e88f27b3Smrg uint32_t type; 61e88f27b3Smrg uint32_t suffix0; 62e88f27b3Smrg uint32_t suffix1; 63e88f27b3Smrg uint32_t *ptr; 64e88f27b3Smrg uint32_t *bgn; 65e88f27b3Smrg int bo_next; 66e88f27b3Smrg int bo_nr; 67e88f27b3Smrg struct nouveau_bo *bos[]; 68e88f27b3Smrg}; 69e88f27b3Smrg 70e88f27b3Smrgstatic inline struct nouveau_pushbuf_priv * 71e88f27b3Smrgnouveau_pushbuf(struct nouveau_pushbuf *push) 72e88f27b3Smrg{ 73e88f27b3Smrg return (struct nouveau_pushbuf_priv *)push; 74e88f27b3Smrg} 75e88f27b3Smrg 76e88f27b3Smrgstatic int pushbuf_validate(struct nouveau_pushbuf *, bool); 77e88f27b3Smrgstatic int pushbuf_flush(struct nouveau_pushbuf *); 78e88f27b3Smrg 79e88f27b3Smrgstatic bool 80e88f27b3Smrgpushbuf_kref_fits(struct nouveau_pushbuf *push, struct nouveau_bo *bo, 81e88f27b3Smrg uint32_t *domains) 82e88f27b3Smrg{ 83e88f27b3Smrg struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(push); 84e88f27b3Smrg struct nouveau_pushbuf_krec *krec = nvpb->krec; 85e88f27b3Smrg struct nouveau_device *dev = push->client->device; 86e88f27b3Smrg struct nouveau_bo *kbo; 87e88f27b3Smrg struct drm_nouveau_gem_pushbuf_bo *kref; 88e88f27b3Smrg int i; 89e88f27b3Smrg 90e88f27b3Smrg /* VRAM is the only valid domain. GART and VRAM|GART buffers 91e88f27b3Smrg * are all accounted to GART, so if this doesn't fit in VRAM 92e88f27b3Smrg * straight up, a flush is needed. 93e88f27b3Smrg */ 94e88f27b3Smrg if (*domains == NOUVEAU_GEM_DOMAIN_VRAM) { 95e88f27b3Smrg if (krec->vram_used + bo->size > dev->vram_limit) 96e88f27b3Smrg return false; 97e88f27b3Smrg krec->vram_used += bo->size; 98e88f27b3Smrg return true; 99e88f27b3Smrg } 100e88f27b3Smrg 101e88f27b3Smrg /* GART or VRAM|GART buffer. Account both of these buffer types 102e88f27b3Smrg * to GART only for the moment, which simplifies things. If the 103e88f27b3Smrg * buffer can fit already, we're done here. 104e88f27b3Smrg */ 105e88f27b3Smrg if (krec->gart_used + bo->size <= dev->gart_limit) { 106e88f27b3Smrg krec->gart_used += bo->size; 107e88f27b3Smrg return true; 108e88f27b3Smrg } 109e88f27b3Smrg 110e88f27b3Smrg /* Ran out of GART space, if it's a VRAM|GART buffer and it'll 111e88f27b3Smrg * fit into available VRAM, turn it into a VRAM buffer 112e88f27b3Smrg */ 113e88f27b3Smrg if ((*domains & NOUVEAU_GEM_DOMAIN_VRAM) && 114e88f27b3Smrg krec->vram_used + bo->size <= dev->vram_limit) { 115e88f27b3Smrg *domains &= NOUVEAU_GEM_DOMAIN_VRAM; 116e88f27b3Smrg krec->vram_used += bo->size; 117e88f27b3Smrg return true; 118e88f27b3Smrg } 119e88f27b3Smrg 120e88f27b3Smrg /* Still couldn't fit the buffer in anywhere, so as a last resort; 121e88f27b3Smrg * scan the buffer list for VRAM|GART buffers and turn them into 122e88f27b3Smrg * VRAM buffers until we have enough space in GART for this one 123e88f27b3Smrg */ 124e88f27b3Smrg kref = krec->buffer; 125e88f27b3Smrg for (i = 0; i < krec->nr_buffer; i++, kref++) { 126e88f27b3Smrg if (!(kref->valid_domains & NOUVEAU_GEM_DOMAIN_GART)) 127e88f27b3Smrg continue; 128e88f27b3Smrg 129e88f27b3Smrg kbo = (void *)(unsigned long)kref->user_priv; 130e88f27b3Smrg if (!(kref->valid_domains & NOUVEAU_GEM_DOMAIN_VRAM) || 131e88f27b3Smrg krec->vram_used + kbo->size > dev->vram_limit) 132e88f27b3Smrg continue; 133e88f27b3Smrg 134e88f27b3Smrg kref->valid_domains &= NOUVEAU_GEM_DOMAIN_VRAM; 135e88f27b3Smrg krec->gart_used -= kbo->size; 136e88f27b3Smrg krec->vram_used += kbo->size; 137e88f27b3Smrg if (krec->gart_used + bo->size <= dev->gart_limit) { 138e88f27b3Smrg krec->gart_used += bo->size; 139e88f27b3Smrg return true; 140e88f27b3Smrg } 141e88f27b3Smrg } 142e88f27b3Smrg 143e88f27b3Smrg /* Couldn't resolve a placement, need to force a flush */ 144e88f27b3Smrg return false; 145e88f27b3Smrg} 146e88f27b3Smrg 147e88f27b3Smrgstatic struct drm_nouveau_gem_pushbuf_bo * 148e88f27b3Smrgpushbuf_kref(struct nouveau_pushbuf *push, struct nouveau_bo *bo, 149e88f27b3Smrg uint32_t flags) 150e88f27b3Smrg{ 151e88f27b3Smrg struct nouveau_device *dev = push->client->device; 152e88f27b3Smrg struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(push); 153e88f27b3Smrg struct nouveau_pushbuf_krec *krec = nvpb->krec; 154e88f27b3Smrg struct nouveau_pushbuf *fpush; 155e88f27b3Smrg struct drm_nouveau_gem_pushbuf_bo *kref; 156e88f27b3Smrg uint32_t domains, domains_wr, domains_rd; 157e88f27b3Smrg 158e88f27b3Smrg domains = 0; 159e88f27b3Smrg if (flags & NOUVEAU_BO_VRAM) 160e88f27b3Smrg domains |= NOUVEAU_GEM_DOMAIN_VRAM; 161e88f27b3Smrg if (flags & NOUVEAU_BO_GART) 162e88f27b3Smrg domains |= NOUVEAU_GEM_DOMAIN_GART; 163e88f27b3Smrg domains_wr = domains * !!(flags & NOUVEAU_BO_WR); 164e88f27b3Smrg domains_rd = domains * !!(flags & NOUVEAU_BO_RD); 165e88f27b3Smrg 166e88f27b3Smrg /* if buffer is referenced on another pushbuf that is owned by the 167e88f27b3Smrg * same client, we need to flush the other pushbuf first to ensure 168e88f27b3Smrg * the correct ordering of commands 169e88f27b3Smrg */ 170e88f27b3Smrg fpush = cli_push_get(push->client, bo); 171e88f27b3Smrg if (fpush && fpush != push) 172e88f27b3Smrg pushbuf_flush(fpush); 173e88f27b3Smrg 174e88f27b3Smrg kref = cli_kref_get(push->client, bo); 175e88f27b3Smrg if (kref) { 176e88f27b3Smrg /* possible conflict in memory types - flush and retry */ 177e88f27b3Smrg if (!(kref->valid_domains & domains)) 178e88f27b3Smrg return NULL; 179e88f27b3Smrg 180e88f27b3Smrg /* VRAM|GART buffer turning into a VRAM buffer. Make sure 181e88f27b3Smrg * it'll fit in VRAM and force a flush if not. 182e88f27b3Smrg */ 183e88f27b3Smrg if ((kref->valid_domains & NOUVEAU_GEM_DOMAIN_GART) && 184e88f27b3Smrg ( domains == NOUVEAU_GEM_DOMAIN_VRAM)) { 185e88f27b3Smrg if (krec->vram_used + bo->size > dev->vram_limit) 186e88f27b3Smrg return NULL; 187e88f27b3Smrg krec->vram_used += bo->size; 188e88f27b3Smrg krec->gart_used -= bo->size; 189e88f27b3Smrg } 190e88f27b3Smrg 191e88f27b3Smrg kref->valid_domains &= domains; 192e88f27b3Smrg kref->write_domains |= domains_wr; 193e88f27b3Smrg kref->read_domains |= domains_rd; 194e88f27b3Smrg } else { 195e88f27b3Smrg if (krec->nr_buffer == NOUVEAU_GEM_MAX_BUFFERS || 196e88f27b3Smrg !pushbuf_kref_fits(push, bo, &domains)) 197e88f27b3Smrg return NULL; 198e88f27b3Smrg 199e88f27b3Smrg kref = &krec->buffer[krec->nr_buffer++]; 200e88f27b3Smrg kref->user_priv = (unsigned long)bo; 201e88f27b3Smrg kref->handle = bo->handle; 202e88f27b3Smrg kref->valid_domains = domains; 203e88f27b3Smrg kref->write_domains = domains_wr; 204e88f27b3Smrg kref->read_domains = domains_rd; 205e88f27b3Smrg kref->presumed.valid = 1; 206e88f27b3Smrg kref->presumed.offset = bo->offset; 207e88f27b3Smrg if (bo->flags & NOUVEAU_BO_VRAM) 208e88f27b3Smrg kref->presumed.domain = NOUVEAU_GEM_DOMAIN_VRAM; 209e88f27b3Smrg else 210e88f27b3Smrg kref->presumed.domain = NOUVEAU_GEM_DOMAIN_GART; 211e88f27b3Smrg 212e88f27b3Smrg cli_kref_set(push->client, bo, kref, push); 213e88f27b3Smrg atomic_inc(&nouveau_bo(bo)->refcnt); 214e88f27b3Smrg } 215e88f27b3Smrg 216e88f27b3Smrg return kref; 217e88f27b3Smrg} 218e88f27b3Smrg 219e88f27b3Smrgstatic uint32_t 220e88f27b3Smrgpushbuf_krel(struct nouveau_pushbuf *push, struct nouveau_bo *bo, 221e88f27b3Smrg uint32_t data, uint32_t flags, uint32_t vor, uint32_t tor) 222e88f27b3Smrg{ 223e88f27b3Smrg struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(push); 224e88f27b3Smrg struct nouveau_pushbuf_krec *krec = nvpb->krec; 225e88f27b3Smrg struct drm_nouveau_gem_pushbuf_reloc *krel; 226e88f27b3Smrg struct drm_nouveau_gem_pushbuf_bo *pkref; 227e88f27b3Smrg struct drm_nouveau_gem_pushbuf_bo *bkref; 228e88f27b3Smrg uint32_t reloc = data; 229e88f27b3Smrg 230e88f27b3Smrg pkref = cli_kref_get(push->client, nvpb->bo); 231e88f27b3Smrg bkref = cli_kref_get(push->client, bo); 232e88f27b3Smrg krel = &krec->reloc[krec->nr_reloc++]; 233e88f27b3Smrg 234e6188e58Smrg assert(pkref); 235e6188e58Smrg assert(bkref); 236e88f27b3Smrg krel->reloc_bo_index = pkref - krec->buffer; 237e88f27b3Smrg krel->reloc_bo_offset = (push->cur - nvpb->ptr) * 4; 238e88f27b3Smrg krel->bo_index = bkref - krec->buffer; 239e88f27b3Smrg krel->flags = 0; 240e88f27b3Smrg krel->data = data; 241e88f27b3Smrg krel->vor = vor; 242e88f27b3Smrg krel->tor = tor; 243e88f27b3Smrg 244e88f27b3Smrg if (flags & NOUVEAU_BO_LOW) { 245e88f27b3Smrg reloc = (bkref->presumed.offset + data); 246e88f27b3Smrg krel->flags |= NOUVEAU_GEM_RELOC_LOW; 247e88f27b3Smrg } else 248e88f27b3Smrg if (flags & NOUVEAU_BO_HIGH) { 249e88f27b3Smrg reloc = (bkref->presumed.offset + data) >> 32; 250e88f27b3Smrg krel->flags |= NOUVEAU_GEM_RELOC_HIGH; 251e88f27b3Smrg } 252e88f27b3Smrg if (flags & NOUVEAU_BO_OR) { 253e88f27b3Smrg if (bkref->presumed.domain & NOUVEAU_GEM_DOMAIN_VRAM) 254e88f27b3Smrg reloc |= vor; 255e88f27b3Smrg else 256e88f27b3Smrg reloc |= tor; 257e88f27b3Smrg krel->flags |= NOUVEAU_GEM_RELOC_OR; 258e88f27b3Smrg } 259e88f27b3Smrg 260e88f27b3Smrg return reloc; 261e88f27b3Smrg} 262e88f27b3Smrg 263e88f27b3Smrgstatic void 264e88f27b3Smrgpushbuf_dump(struct nouveau_pushbuf_krec *krec, int krec_id, int chid) 265e88f27b3Smrg{ 266e88f27b3Smrg struct drm_nouveau_gem_pushbuf_reloc *krel; 267e88f27b3Smrg struct drm_nouveau_gem_pushbuf_push *kpsh; 268e88f27b3Smrg struct drm_nouveau_gem_pushbuf_bo *kref; 269e88f27b3Smrg struct nouveau_bo *bo; 270e88f27b3Smrg uint32_t *bgn, *end; 271e88f27b3Smrg int i; 272e88f27b3Smrg 273e88f27b3Smrg err("ch%d: krec %d pushes %d bufs %d relocs %d\n", chid, 274e88f27b3Smrg krec_id, krec->nr_push, krec->nr_buffer, krec->nr_reloc); 275e88f27b3Smrg 276e88f27b3Smrg kref = krec->buffer; 277e88f27b3Smrg for (i = 0; i < krec->nr_buffer; i++, kref++) { 27849ef06a4Smrg bo = (void *)(uintptr_t)kref->user_priv; 27949ef06a4Smrg err("ch%d: buf %08x %08x %08x %08x %08x %p 0x%"PRIx64" 0x%"PRIx64"\n", chid, i, 280e88f27b3Smrg kref->handle, kref->valid_domains, 28149ef06a4Smrg kref->read_domains, kref->write_domains, bo->map, bo->offset, bo->size); 282e88f27b3Smrg } 283e88f27b3Smrg 284e88f27b3Smrg krel = krec->reloc; 285e88f27b3Smrg for (i = 0; i < krec->nr_reloc; i++, krel++) { 286e88f27b3Smrg err("ch%d: rel %08x %08x %08x %08x %08x %08x %08x\n", 287e88f27b3Smrg chid, krel->reloc_bo_index, krel->reloc_bo_offset, 288e88f27b3Smrg krel->bo_index, krel->flags, krel->data, 289e88f27b3Smrg krel->vor, krel->tor); 290e88f27b3Smrg } 291e88f27b3Smrg 292e88f27b3Smrg kpsh = krec->push; 293e88f27b3Smrg for (i = 0; i < krec->nr_push; i++, kpsh++) { 294e88f27b3Smrg kref = krec->buffer + kpsh->bo_index; 295e88f27b3Smrg bo = (void *)(unsigned long)kref->user_priv; 296e88f27b3Smrg bgn = (uint32_t *)((char *)bo->map + kpsh->offset); 2974babd585Smrg end = bgn + ((kpsh->length & 0x7fffff) /4); 298e88f27b3Smrg 2994babd585Smrg err("ch%d: psh %s%08x %010llx %010llx\n", chid, 3004babd585Smrg bo->map ? "" : "(unmapped) ", kpsh->bo_index, 301e88f27b3Smrg (unsigned long long)kpsh->offset, 302e88f27b3Smrg (unsigned long long)(kpsh->offset + kpsh->length)); 3034babd585Smrg if (!bo->map) 3044babd585Smrg continue; 305e88f27b3Smrg while (bgn < end) 306e88f27b3Smrg err("\t0x%08x\n", *bgn++); 307e88f27b3Smrg } 308e88f27b3Smrg} 309e88f27b3Smrg 310e88f27b3Smrgstatic int 311e88f27b3Smrgpushbuf_submit(struct nouveau_pushbuf *push, struct nouveau_object *chan) 312e88f27b3Smrg{ 313e88f27b3Smrg struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(push); 314e88f27b3Smrg struct nouveau_pushbuf_krec *krec = nvpb->list; 315e88f27b3Smrg struct nouveau_device *dev = push->client->device; 3163f012e29Smrg struct nouveau_drm *drm = nouveau_drm(&dev->object); 317e88f27b3Smrg struct drm_nouveau_gem_pushbuf_bo_presumed *info; 318e88f27b3Smrg struct drm_nouveau_gem_pushbuf_bo *kref; 319e88f27b3Smrg struct drm_nouveau_gem_pushbuf req; 320e88f27b3Smrg struct nouveau_fifo *fifo = chan->data; 321e88f27b3Smrg struct nouveau_bo *bo; 322e88f27b3Smrg int krec_id = 0; 323e88f27b3Smrg int ret = 0, i; 324e88f27b3Smrg 325e88f27b3Smrg if (chan->oclass != NOUVEAU_FIFO_CHANNEL_CLASS) 326e88f27b3Smrg return -EINVAL; 327e88f27b3Smrg 328e88f27b3Smrg if (push->kick_notify) 329e88f27b3Smrg push->kick_notify(push); 330e88f27b3Smrg 331e88f27b3Smrg nouveau_pushbuf_data(push, NULL, 0, 0); 332e88f27b3Smrg 333e88f27b3Smrg while (krec && krec->nr_push) { 334e88f27b3Smrg req.channel = fifo->channel; 335e88f27b3Smrg req.nr_buffers = krec->nr_buffer; 336e88f27b3Smrg req.buffers = (uint64_t)(unsigned long)krec->buffer; 337e88f27b3Smrg req.nr_relocs = krec->nr_reloc; 338e88f27b3Smrg req.nr_push = krec->nr_push; 339e88f27b3Smrg req.relocs = (uint64_t)(unsigned long)krec->reloc; 340e88f27b3Smrg req.push = (uint64_t)(unsigned long)krec->push; 341e88f27b3Smrg req.suffix0 = nvpb->suffix0; 342e88f27b3Smrg req.suffix1 = nvpb->suffix1; 343e88f27b3Smrg req.vram_available = 0; /* for valgrind */ 3444babd585Smrg if (dbg_on(1)) 3454babd585Smrg req.vram_available |= NOUVEAU_GEM_PUSHBUF_SYNC; 346e88f27b3Smrg req.gart_available = 0; 347e88f27b3Smrg 348e88f27b3Smrg if (dbg_on(0)) 349e88f27b3Smrg pushbuf_dump(krec, krec_id++, fifo->channel); 350e88f27b3Smrg 351e88f27b3Smrg#ifndef SIMULATE 3523f012e29Smrg ret = drmCommandWriteRead(drm->fd, DRM_NOUVEAU_GEM_PUSHBUF, 353e88f27b3Smrg &req, sizeof(req)); 354e88f27b3Smrg nvpb->suffix0 = req.suffix0; 355e88f27b3Smrg nvpb->suffix1 = req.suffix1; 356e88f27b3Smrg dev->vram_limit = (req.vram_available * 357e88f27b3Smrg nouveau_device(dev)->vram_limit_percent) / 100; 358e88f27b3Smrg dev->gart_limit = (req.gart_available * 359e88f27b3Smrg nouveau_device(dev)->gart_limit_percent) / 100; 360e88f27b3Smrg#else 361e88f27b3Smrg if (dbg_on(31)) 362e88f27b3Smrg ret = -EINVAL; 363e88f27b3Smrg#endif 364e88f27b3Smrg 365e88f27b3Smrg if (ret) { 366e88f27b3Smrg err("kernel rejected pushbuf: %s\n", strerror(-ret)); 367e88f27b3Smrg pushbuf_dump(krec, krec_id++, fifo->channel); 368e88f27b3Smrg break; 369e88f27b3Smrg } 370e88f27b3Smrg 371e88f27b3Smrg kref = krec->buffer; 372e88f27b3Smrg for (i = 0; i < krec->nr_buffer; i++, kref++) { 373e88f27b3Smrg bo = (void *)(unsigned long)kref->user_priv; 374e88f27b3Smrg 375e88f27b3Smrg info = &kref->presumed; 376e88f27b3Smrg if (!info->valid) { 377e88f27b3Smrg bo->flags &= ~NOUVEAU_BO_APER; 378e88f27b3Smrg if (info->domain == NOUVEAU_GEM_DOMAIN_VRAM) 379e88f27b3Smrg bo->flags |= NOUVEAU_BO_VRAM; 380e88f27b3Smrg else 381e88f27b3Smrg bo->flags |= NOUVEAU_BO_GART; 382e88f27b3Smrg bo->offset = info->offset; 383e88f27b3Smrg } 384e88f27b3Smrg 385e88f27b3Smrg if (kref->write_domains) 386e88f27b3Smrg nouveau_bo(bo)->access |= NOUVEAU_BO_WR; 387e88f27b3Smrg if (kref->read_domains) 388e88f27b3Smrg nouveau_bo(bo)->access |= NOUVEAU_BO_RD; 389e88f27b3Smrg } 390e88f27b3Smrg 391e88f27b3Smrg krec = krec->next; 392e88f27b3Smrg } 393e88f27b3Smrg 394e88f27b3Smrg return ret; 395e88f27b3Smrg} 396e88f27b3Smrg 397e88f27b3Smrgstatic int 398e88f27b3Smrgpushbuf_flush(struct nouveau_pushbuf *push) 399e88f27b3Smrg{ 400e88f27b3Smrg struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(push); 401e88f27b3Smrg struct nouveau_pushbuf_krec *krec = nvpb->krec; 402e88f27b3Smrg struct drm_nouveau_gem_pushbuf_bo *kref; 403e88f27b3Smrg struct nouveau_bufctx *bctx, *btmp; 404e88f27b3Smrg struct nouveau_bo *bo; 405e88f27b3Smrg int ret = 0, i; 406e88f27b3Smrg 407e88f27b3Smrg if (push->channel) { 408e88f27b3Smrg ret = pushbuf_submit(push, push->channel); 409e88f27b3Smrg } else { 410e88f27b3Smrg nouveau_pushbuf_data(push, NULL, 0, 0); 411e88f27b3Smrg krec->next = malloc(sizeof(*krec)); 412e88f27b3Smrg nvpb->krec = krec->next; 413e88f27b3Smrg } 414e88f27b3Smrg 415e88f27b3Smrg kref = krec->buffer; 416e88f27b3Smrg for (i = 0; i < krec->nr_buffer; i++, kref++) { 417e88f27b3Smrg bo = (void *)(unsigned long)kref->user_priv; 418e88f27b3Smrg cli_kref_set(push->client, bo, NULL, NULL); 419e88f27b3Smrg if (push->channel) 420e88f27b3Smrg nouveau_bo_ref(NULL, &bo); 421e88f27b3Smrg } 422e88f27b3Smrg 423e88f27b3Smrg krec = nvpb->krec; 424e88f27b3Smrg krec->vram_used = 0; 425e88f27b3Smrg krec->gart_used = 0; 426e88f27b3Smrg krec->nr_buffer = 0; 427e88f27b3Smrg krec->nr_reloc = 0; 428e88f27b3Smrg krec->nr_push = 0; 429e88f27b3Smrg 430e88f27b3Smrg DRMLISTFOREACHENTRYSAFE(bctx, btmp, &nvpb->bctx_list, head) { 431e88f27b3Smrg DRMLISTJOIN(&bctx->current, &bctx->pending); 432e88f27b3Smrg DRMINITLISTHEAD(&bctx->current); 433e88f27b3Smrg DRMLISTDELINIT(&bctx->head); 434e88f27b3Smrg } 435e88f27b3Smrg 436e88f27b3Smrg return ret; 437e88f27b3Smrg} 438e88f27b3Smrg 439e88f27b3Smrgstatic void 440e88f27b3Smrgpushbuf_refn_fail(struct nouveau_pushbuf *push, int sref, int srel) 441e88f27b3Smrg{ 442e88f27b3Smrg struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(push); 443e88f27b3Smrg struct nouveau_pushbuf_krec *krec = nvpb->krec; 444e88f27b3Smrg struct drm_nouveau_gem_pushbuf_bo *kref; 445e88f27b3Smrg 446e88f27b3Smrg kref = krec->buffer + sref; 447e88f27b3Smrg while (krec->nr_buffer-- > sref) { 448e88f27b3Smrg struct nouveau_bo *bo = (void *)(unsigned long)kref->user_priv; 449e88f27b3Smrg cli_kref_set(push->client, bo, NULL, NULL); 450e88f27b3Smrg nouveau_bo_ref(NULL, &bo); 451e88f27b3Smrg kref++; 452e88f27b3Smrg } 453e88f27b3Smrg krec->nr_buffer = sref; 454e88f27b3Smrg krec->nr_reloc = srel; 455e88f27b3Smrg} 456e88f27b3Smrg 457e88f27b3Smrgstatic int 458e88f27b3Smrgpushbuf_refn(struct nouveau_pushbuf *push, bool retry, 459e88f27b3Smrg struct nouveau_pushbuf_refn *refs, int nr) 460e88f27b3Smrg{ 461e88f27b3Smrg struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(push); 462e88f27b3Smrg struct nouveau_pushbuf_krec *krec = nvpb->krec; 463e88f27b3Smrg struct drm_nouveau_gem_pushbuf_bo *kref; 464e88f27b3Smrg int sref = krec->nr_buffer; 465e88f27b3Smrg int ret = 0, i; 466e88f27b3Smrg 467e88f27b3Smrg for (i = 0; i < nr; i++) { 468e88f27b3Smrg kref = pushbuf_kref(push, refs[i].bo, refs[i].flags); 469e88f27b3Smrg if (!kref) { 470e88f27b3Smrg ret = -ENOSPC; 471e88f27b3Smrg break; 472e88f27b3Smrg } 473e88f27b3Smrg } 474e88f27b3Smrg 475e88f27b3Smrg if (ret) { 476e88f27b3Smrg pushbuf_refn_fail(push, sref, krec->nr_reloc); 477e88f27b3Smrg if (retry) { 478e88f27b3Smrg pushbuf_flush(push); 479e88f27b3Smrg nouveau_pushbuf_space(push, 0, 0, 0); 480e88f27b3Smrg return pushbuf_refn(push, false, refs, nr); 481e88f27b3Smrg } 482e88f27b3Smrg } 483e88f27b3Smrg 484e88f27b3Smrg return ret; 485e88f27b3Smrg} 486e88f27b3Smrg 487e88f27b3Smrgstatic int 488e88f27b3Smrgpushbuf_validate(struct nouveau_pushbuf *push, bool retry) 489e88f27b3Smrg{ 490e88f27b3Smrg struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(push); 491e88f27b3Smrg struct nouveau_pushbuf_krec *krec = nvpb->krec; 492e88f27b3Smrg struct drm_nouveau_gem_pushbuf_bo *kref; 493e88f27b3Smrg struct nouveau_bufctx *bctx = push->bufctx; 494e88f27b3Smrg struct nouveau_bufref *bref; 495e88f27b3Smrg int relocs = bctx ? bctx->relocs * 2: 0; 496e88f27b3Smrg int sref, srel, ret; 497e88f27b3Smrg 498e88f27b3Smrg ret = nouveau_pushbuf_space(push, relocs, relocs, 0); 499e88f27b3Smrg if (ret || bctx == NULL) 500e88f27b3Smrg return ret; 501e88f27b3Smrg 502e88f27b3Smrg sref = krec->nr_buffer; 503e88f27b3Smrg srel = krec->nr_reloc; 504e88f27b3Smrg 505e88f27b3Smrg DRMLISTDEL(&bctx->head); 506e88f27b3Smrg DRMLISTADD(&bctx->head, &nvpb->bctx_list); 507e88f27b3Smrg 508e88f27b3Smrg DRMLISTFOREACHENTRY(bref, &bctx->pending, thead) { 509e88f27b3Smrg kref = pushbuf_kref(push, bref->bo, bref->flags); 510e88f27b3Smrg if (!kref) { 511e88f27b3Smrg ret = -ENOSPC; 512e88f27b3Smrg break; 513e88f27b3Smrg } 514e88f27b3Smrg 515e88f27b3Smrg if (bref->packet) { 516e88f27b3Smrg pushbuf_krel(push, bref->bo, bref->packet, 0, 0, 0); 517e88f27b3Smrg *push->cur++ = 0; 518e88f27b3Smrg pushbuf_krel(push, bref->bo, bref->data, bref->flags, 519e88f27b3Smrg bref->vor, bref->tor); 520e88f27b3Smrg *push->cur++ = 0; 521e88f27b3Smrg } 522e88f27b3Smrg } 523e88f27b3Smrg 524e88f27b3Smrg DRMLISTJOIN(&bctx->pending, &bctx->current); 525e88f27b3Smrg DRMINITLISTHEAD(&bctx->pending); 526e88f27b3Smrg 527e88f27b3Smrg if (ret) { 528e88f27b3Smrg pushbuf_refn_fail(push, sref, srel); 529e88f27b3Smrg if (retry) { 530e88f27b3Smrg pushbuf_flush(push); 531e88f27b3Smrg return pushbuf_validate(push, false); 532e88f27b3Smrg } 533e88f27b3Smrg } 534e88f27b3Smrg 535e88f27b3Smrg return ret; 536e88f27b3Smrg} 537e88f27b3Smrg 5387cdc0497Smrgdrm_public int 539e88f27b3Smrgnouveau_pushbuf_new(struct nouveau_client *client, struct nouveau_object *chan, 540e88f27b3Smrg int nr, uint32_t size, bool immediate, 541e88f27b3Smrg struct nouveau_pushbuf **ppush) 542e88f27b3Smrg{ 5433f012e29Smrg struct nouveau_drm *drm = nouveau_drm(&client->device->object); 544e88f27b3Smrg struct nouveau_fifo *fifo = chan->data; 545e88f27b3Smrg struct nouveau_pushbuf_priv *nvpb; 546e88f27b3Smrg struct nouveau_pushbuf *push; 547e88f27b3Smrg struct drm_nouveau_gem_pushbuf req = {}; 548e88f27b3Smrg int ret; 549e88f27b3Smrg 550e88f27b3Smrg if (chan->oclass != NOUVEAU_FIFO_CHANNEL_CLASS) 551e88f27b3Smrg return -EINVAL; 552e88f27b3Smrg 553e88f27b3Smrg /* nop pushbuf call, to get the current "return to main" sequence 554e88f27b3Smrg * we need to append to the pushbuf on early chipsets 555e88f27b3Smrg */ 556e88f27b3Smrg req.channel = fifo->channel; 557e88f27b3Smrg req.nr_push = 0; 5583f012e29Smrg ret = drmCommandWriteRead(drm->fd, DRM_NOUVEAU_GEM_PUSHBUF, 559e88f27b3Smrg &req, sizeof(req)); 560e88f27b3Smrg if (ret) 561e88f27b3Smrg return ret; 562e88f27b3Smrg 563e88f27b3Smrg nvpb = calloc(1, sizeof(*nvpb) + nr * sizeof(*nvpb->bos)); 564e88f27b3Smrg if (!nvpb) 565e88f27b3Smrg return -ENOMEM; 566e88f27b3Smrg 567e88f27b3Smrg#ifndef SIMULATE 568e88f27b3Smrg nvpb->suffix0 = req.suffix0; 569e88f27b3Smrg nvpb->suffix1 = req.suffix1; 570e88f27b3Smrg#else 571e88f27b3Smrg nvpb->suffix0 = 0xffffffff; 572e88f27b3Smrg nvpb->suffix1 = 0xffffffff; 573e88f27b3Smrg#endif 574e88f27b3Smrg nvpb->krec = calloc(1, sizeof(*nvpb->krec)); 575e88f27b3Smrg nvpb->list = nvpb->krec; 576e88f27b3Smrg if (!nvpb->krec) { 577e88f27b3Smrg free(nvpb); 578e88f27b3Smrg return -ENOMEM; 579e88f27b3Smrg } 580e88f27b3Smrg 581e88f27b3Smrg push = &nvpb->base; 582e88f27b3Smrg push->client = client; 583e88f27b3Smrg push->channel = immediate ? chan : NULL; 584e88f27b3Smrg push->flags = NOUVEAU_BO_RD; 585e88f27b3Smrg if (fifo->pushbuf & NOUVEAU_GEM_DOMAIN_GART) { 586e88f27b3Smrg push->flags |= NOUVEAU_BO_GART; 587e88f27b3Smrg nvpb->type = NOUVEAU_BO_GART; 588e88f27b3Smrg } else 589e88f27b3Smrg if (fifo->pushbuf & NOUVEAU_GEM_DOMAIN_VRAM) { 590e88f27b3Smrg push->flags |= NOUVEAU_BO_VRAM; 591e88f27b3Smrg nvpb->type = NOUVEAU_BO_VRAM; 592e88f27b3Smrg } 593e88f27b3Smrg nvpb->type |= NOUVEAU_BO_MAP; 594e88f27b3Smrg 595e88f27b3Smrg for (nvpb->bo_nr = 0; nvpb->bo_nr < nr; nvpb->bo_nr++) { 596e88f27b3Smrg ret = nouveau_bo_new(client->device, nvpb->type, 0, size, 597e88f27b3Smrg NULL, &nvpb->bos[nvpb->bo_nr]); 598e88f27b3Smrg if (ret) { 599e88f27b3Smrg nouveau_pushbuf_del(&push); 600e88f27b3Smrg return ret; 601e88f27b3Smrg } 602e88f27b3Smrg } 603e88f27b3Smrg 604e88f27b3Smrg DRMINITLISTHEAD(&nvpb->bctx_list); 605e88f27b3Smrg *ppush = push; 606e88f27b3Smrg return 0; 607e88f27b3Smrg} 608e88f27b3Smrg 6097cdc0497Smrgdrm_public void 610e88f27b3Smrgnouveau_pushbuf_del(struct nouveau_pushbuf **ppush) 611e88f27b3Smrg{ 612e88f27b3Smrg struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(*ppush); 613e88f27b3Smrg if (nvpb) { 614e88f27b3Smrg struct drm_nouveau_gem_pushbuf_bo *kref; 615e88f27b3Smrg struct nouveau_pushbuf_krec *krec; 616e88f27b3Smrg while ((krec = nvpb->list)) { 617e88f27b3Smrg kref = krec->buffer; 618e88f27b3Smrg while (krec->nr_buffer--) { 619e88f27b3Smrg unsigned long priv = kref++->user_priv; 620e88f27b3Smrg struct nouveau_bo *bo = (void *)priv; 621e88f27b3Smrg cli_kref_set(nvpb->base.client, bo, NULL, NULL); 622e88f27b3Smrg nouveau_bo_ref(NULL, &bo); 623e88f27b3Smrg } 624e88f27b3Smrg nvpb->list = krec->next; 625e88f27b3Smrg free(krec); 626e88f27b3Smrg } 627e88f27b3Smrg while (nvpb->bo_nr--) 628e88f27b3Smrg nouveau_bo_ref(NULL, &nvpb->bos[nvpb->bo_nr]); 629e88f27b3Smrg nouveau_bo_ref(NULL, &nvpb->bo); 630e88f27b3Smrg free(nvpb); 631e88f27b3Smrg } 632e88f27b3Smrg *ppush = NULL; 633e88f27b3Smrg} 634e88f27b3Smrg 6357cdc0497Smrgdrm_public struct nouveau_bufctx * 636e88f27b3Smrgnouveau_pushbuf_bufctx(struct nouveau_pushbuf *push, struct nouveau_bufctx *ctx) 637e88f27b3Smrg{ 638e88f27b3Smrg struct nouveau_bufctx *prev = push->bufctx; 639e88f27b3Smrg push->bufctx = ctx; 640e88f27b3Smrg return prev; 641e88f27b3Smrg} 642e88f27b3Smrg 6437cdc0497Smrgdrm_public int 644e88f27b3Smrgnouveau_pushbuf_space(struct nouveau_pushbuf *push, 645e88f27b3Smrg uint32_t dwords, uint32_t relocs, uint32_t pushes) 646e88f27b3Smrg{ 647e88f27b3Smrg struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(push); 648e88f27b3Smrg struct nouveau_pushbuf_krec *krec = nvpb->krec; 649e88f27b3Smrg struct nouveau_client *client = push->client; 650e88f27b3Smrg struct nouveau_bo *bo = NULL; 651e88f27b3Smrg bool flushed = false; 652e88f27b3Smrg int ret = 0; 653e88f27b3Smrg 654e88f27b3Smrg /* switch to next buffer if insufficient space in the current one */ 655e88f27b3Smrg if (push->cur + dwords >= push->end) { 656e88f27b3Smrg if (nvpb->bo_next < nvpb->bo_nr) { 657e88f27b3Smrg nouveau_bo_ref(nvpb->bos[nvpb->bo_next++], &bo); 658e88f27b3Smrg if (nvpb->bo_next == nvpb->bo_nr && push->channel) 659e88f27b3Smrg nvpb->bo_next = 0; 660e88f27b3Smrg } else { 661e88f27b3Smrg ret = nouveau_bo_new(client->device, nvpb->type, 0, 662e88f27b3Smrg nvpb->bos[0]->size, NULL, &bo); 663e88f27b3Smrg if (ret) 664e88f27b3Smrg return ret; 665e88f27b3Smrg } 666e88f27b3Smrg } 667e88f27b3Smrg 668e88f27b3Smrg /* make sure there's always enough space to queue up the pending 669e88f27b3Smrg * data in the pushbuf proper 670e88f27b3Smrg */ 671e88f27b3Smrg pushes++; 672e88f27b3Smrg 673e88f27b3Smrg /* need to flush if we've run out of space on an immediate pushbuf, 674e88f27b3Smrg * if the new buffer won't fit, or if the kernel push/reloc limits 675e88f27b3Smrg * have been hit 676e88f27b3Smrg */ 677e88f27b3Smrg if ((bo && ( push->channel || 678e88f27b3Smrg !pushbuf_kref(push, bo, push->flags))) || 679e88f27b3Smrg krec->nr_reloc + relocs >= NOUVEAU_GEM_MAX_RELOCS || 680e88f27b3Smrg krec->nr_push + pushes >= NOUVEAU_GEM_MAX_PUSH) { 681e88f27b3Smrg if (nvpb->bo && krec->nr_buffer) 682e88f27b3Smrg pushbuf_flush(push); 683e88f27b3Smrg flushed = true; 684e88f27b3Smrg } 685e88f27b3Smrg 686e88f27b3Smrg /* if necessary, switch to new buffer */ 687e88f27b3Smrg if (bo) { 688e88f27b3Smrg ret = nouveau_bo_map(bo, NOUVEAU_BO_WR, push->client); 689e88f27b3Smrg if (ret) 690e88f27b3Smrg return ret; 691e88f27b3Smrg 692e88f27b3Smrg nouveau_pushbuf_data(push, NULL, 0, 0); 693e88f27b3Smrg nouveau_bo_ref(bo, &nvpb->bo); 694e88f27b3Smrg nouveau_bo_ref(NULL, &bo); 695e88f27b3Smrg 696e88f27b3Smrg nvpb->bgn = nvpb->bo->map; 697e88f27b3Smrg nvpb->ptr = nvpb->bgn; 698e88f27b3Smrg push->cur = nvpb->bgn; 699e88f27b3Smrg push->end = push->cur + (nvpb->bo->size / 4); 700e88f27b3Smrg push->end -= 2 + push->rsvd_kick; /* space for suffix */ 701e88f27b3Smrg } 702e88f27b3Smrg 703e88f27b3Smrg pushbuf_kref(push, nvpb->bo, push->flags); 704e88f27b3Smrg return flushed ? pushbuf_validate(push, false) : 0; 705e88f27b3Smrg} 706e88f27b3Smrg 7077cdc0497Smrgdrm_public void 708e88f27b3Smrgnouveau_pushbuf_data(struct nouveau_pushbuf *push, struct nouveau_bo *bo, 709e88f27b3Smrg uint64_t offset, uint64_t length) 710e88f27b3Smrg{ 711e88f27b3Smrg struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(push); 712e88f27b3Smrg struct nouveau_pushbuf_krec *krec = nvpb->krec; 713e88f27b3Smrg struct drm_nouveau_gem_pushbuf_push *kpsh; 714e88f27b3Smrg struct drm_nouveau_gem_pushbuf_bo *kref; 715e88f27b3Smrg 716e88f27b3Smrg if (bo != nvpb->bo && nvpb->bgn != push->cur) { 717e88f27b3Smrg if (nvpb->suffix0 || nvpb->suffix1) { 718e88f27b3Smrg *push->cur++ = nvpb->suffix0; 719e88f27b3Smrg *push->cur++ = nvpb->suffix1; 720e88f27b3Smrg } 721e88f27b3Smrg 722e88f27b3Smrg nouveau_pushbuf_data(push, nvpb->bo, 723e88f27b3Smrg (nvpb->bgn - nvpb->ptr) * 4, 724e88f27b3Smrg (push->cur - nvpb->bgn) * 4); 725e88f27b3Smrg nvpb->bgn = push->cur; 726e88f27b3Smrg } 727e88f27b3Smrg 728e88f27b3Smrg if (bo) { 729e88f27b3Smrg kref = cli_kref_get(push->client, bo); 730e6188e58Smrg assert(kref); 731e88f27b3Smrg kpsh = &krec->push[krec->nr_push++]; 732e88f27b3Smrg kpsh->bo_index = kref - krec->buffer; 733e88f27b3Smrg kpsh->offset = offset; 734e88f27b3Smrg kpsh->length = length; 735e88f27b3Smrg } 736e88f27b3Smrg} 737e88f27b3Smrg 7387cdc0497Smrgdrm_public int 739e88f27b3Smrgnouveau_pushbuf_refn(struct nouveau_pushbuf *push, 740e88f27b3Smrg struct nouveau_pushbuf_refn *refs, int nr) 741e88f27b3Smrg{ 742e88f27b3Smrg return pushbuf_refn(push, true, refs, nr); 743e88f27b3Smrg} 744e88f27b3Smrg 7457cdc0497Smrgdrm_public void 746e88f27b3Smrgnouveau_pushbuf_reloc(struct nouveau_pushbuf *push, struct nouveau_bo *bo, 747e88f27b3Smrg uint32_t data, uint32_t flags, uint32_t vor, uint32_t tor) 748e88f27b3Smrg{ 749e88f27b3Smrg *push->cur = pushbuf_krel(push, bo, data, flags, vor, tor); 750e88f27b3Smrg push->cur++; 751e88f27b3Smrg} 752e88f27b3Smrg 7537cdc0497Smrgdrm_public int 754e88f27b3Smrgnouveau_pushbuf_validate(struct nouveau_pushbuf *push) 755e88f27b3Smrg{ 756e88f27b3Smrg return pushbuf_validate(push, true); 757e88f27b3Smrg} 758e88f27b3Smrg 7597cdc0497Smrgdrm_public uint32_t 760e88f27b3Smrgnouveau_pushbuf_refd(struct nouveau_pushbuf *push, struct nouveau_bo *bo) 761e88f27b3Smrg{ 762e88f27b3Smrg struct drm_nouveau_gem_pushbuf_bo *kref; 763e88f27b3Smrg uint32_t flags = 0; 764e88f27b3Smrg 765e88f27b3Smrg if (cli_push_get(push->client, bo) == push) { 766e88f27b3Smrg kref = cli_kref_get(push->client, bo); 767e6188e58Smrg assert(kref); 768e88f27b3Smrg if (kref->read_domains) 769e88f27b3Smrg flags |= NOUVEAU_BO_RD; 770e88f27b3Smrg if (kref->write_domains) 771e88f27b3Smrg flags |= NOUVEAU_BO_WR; 772e88f27b3Smrg } 773e88f27b3Smrg 774e88f27b3Smrg return flags; 775e88f27b3Smrg} 776e88f27b3Smrg 7777cdc0497Smrgdrm_public int 778e88f27b3Smrgnouveau_pushbuf_kick(struct nouveau_pushbuf *push, struct nouveau_object *chan) 779e88f27b3Smrg{ 780e88f27b3Smrg if (!push->channel) 781e88f27b3Smrg return pushbuf_submit(push, chan); 782e88f27b3Smrg pushbuf_flush(push); 783e88f27b3Smrg return pushbuf_validate(push, false); 784e88f27b3Smrg} 7850ed5401bSmrg 7860ed5401bSmrgdrm_public bool 7870ed5401bSmrgnouveau_check_dead_channel(struct nouveau_drm *drm, struct nouveau_object *chan) 7880ed5401bSmrg{ 7890ed5401bSmrg struct drm_nouveau_gem_pushbuf req = {}; 7900ed5401bSmrg struct nouveau_fifo *fifo = chan->data; 7910ed5401bSmrg int ret; 7920ed5401bSmrg 7930ed5401bSmrg req.channel = fifo->channel; 7940ed5401bSmrg req.nr_push = 0; 7950ed5401bSmrg 7960ed5401bSmrg ret = drmCommandWriteRead(drm->fd, DRM_NOUVEAU_GEM_PUSHBUF, 7970ed5401bSmrg &req, sizeof(req)); 7980ed5401bSmrg /* nouveau returns ENODEV once the channel was killed */ 7990ed5401bSmrg return ret == -ENODEV; 8000ed5401bSmrg} 801