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