pushbuf.c revision 3f012e29
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; 3153f012e29Smrg struct nouveau_drm *drm = nouveau_drm(&dev->object); 316e88f27b3Smrg struct drm_nouveau_gem_pushbuf_bo_presumed *info; 317e88f27b3Smrg struct drm_nouveau_gem_pushbuf_bo *kref; 318e88f27b3Smrg struct drm_nouveau_gem_pushbuf req; 319e88f27b3Smrg struct nouveau_fifo *fifo = chan->data; 320e88f27b3Smrg struct nouveau_bo *bo; 321e88f27b3Smrg int krec_id = 0; 322e88f27b3Smrg int ret = 0, i; 323e88f27b3Smrg 324e88f27b3Smrg if (chan->oclass != NOUVEAU_FIFO_CHANNEL_CLASS) 325e88f27b3Smrg return -EINVAL; 326e88f27b3Smrg 327e88f27b3Smrg if (push->kick_notify) 328e88f27b3Smrg push->kick_notify(push); 329e88f27b3Smrg 330e88f27b3Smrg nouveau_pushbuf_data(push, NULL, 0, 0); 331e88f27b3Smrg 332e88f27b3Smrg while (krec && krec->nr_push) { 333e88f27b3Smrg req.channel = fifo->channel; 334e88f27b3Smrg req.nr_buffers = krec->nr_buffer; 335e88f27b3Smrg req.buffers = (uint64_t)(unsigned long)krec->buffer; 336e88f27b3Smrg req.nr_relocs = krec->nr_reloc; 337e88f27b3Smrg req.nr_push = krec->nr_push; 338e88f27b3Smrg req.relocs = (uint64_t)(unsigned long)krec->reloc; 339e88f27b3Smrg req.push = (uint64_t)(unsigned long)krec->push; 340e88f27b3Smrg req.suffix0 = nvpb->suffix0; 341e88f27b3Smrg req.suffix1 = nvpb->suffix1; 342e88f27b3Smrg req.vram_available = 0; /* for valgrind */ 343e88f27b3Smrg req.gart_available = 0; 344e88f27b3Smrg 345e88f27b3Smrg if (dbg_on(0)) 346e88f27b3Smrg pushbuf_dump(krec, krec_id++, fifo->channel); 347e88f27b3Smrg 348e88f27b3Smrg#ifndef SIMULATE 3493f012e29Smrg ret = drmCommandWriteRead(drm->fd, DRM_NOUVEAU_GEM_PUSHBUF, 350e88f27b3Smrg &req, sizeof(req)); 351e88f27b3Smrg nvpb->suffix0 = req.suffix0; 352e88f27b3Smrg nvpb->suffix1 = req.suffix1; 353e88f27b3Smrg dev->vram_limit = (req.vram_available * 354e88f27b3Smrg nouveau_device(dev)->vram_limit_percent) / 100; 355e88f27b3Smrg dev->gart_limit = (req.gart_available * 356e88f27b3Smrg nouveau_device(dev)->gart_limit_percent) / 100; 357e88f27b3Smrg#else 358e88f27b3Smrg if (dbg_on(31)) 359e88f27b3Smrg ret = -EINVAL; 360e88f27b3Smrg#endif 361e88f27b3Smrg 362e88f27b3Smrg if (ret) { 363e88f27b3Smrg err("kernel rejected pushbuf: %s\n", strerror(-ret)); 364e88f27b3Smrg pushbuf_dump(krec, krec_id++, fifo->channel); 365e88f27b3Smrg break; 366e88f27b3Smrg } 367e88f27b3Smrg 368e88f27b3Smrg kref = krec->buffer; 369e88f27b3Smrg for (i = 0; i < krec->nr_buffer; i++, kref++) { 370e88f27b3Smrg bo = (void *)(unsigned long)kref->user_priv; 371e88f27b3Smrg 372e88f27b3Smrg info = &kref->presumed; 373e88f27b3Smrg if (!info->valid) { 374e88f27b3Smrg bo->flags &= ~NOUVEAU_BO_APER; 375e88f27b3Smrg if (info->domain == NOUVEAU_GEM_DOMAIN_VRAM) 376e88f27b3Smrg bo->flags |= NOUVEAU_BO_VRAM; 377e88f27b3Smrg else 378e88f27b3Smrg bo->flags |= NOUVEAU_BO_GART; 379e88f27b3Smrg bo->offset = info->offset; 380e88f27b3Smrg } 381e88f27b3Smrg 382e88f27b3Smrg if (kref->write_domains) 383e88f27b3Smrg nouveau_bo(bo)->access |= NOUVEAU_BO_WR; 384e88f27b3Smrg if (kref->read_domains) 385e88f27b3Smrg nouveau_bo(bo)->access |= NOUVEAU_BO_RD; 386e88f27b3Smrg } 387e88f27b3Smrg 388e88f27b3Smrg krec = krec->next; 389e88f27b3Smrg } 390e88f27b3Smrg 391e88f27b3Smrg return ret; 392e88f27b3Smrg} 393e88f27b3Smrg 394e88f27b3Smrgstatic int 395e88f27b3Smrgpushbuf_flush(struct nouveau_pushbuf *push) 396e88f27b3Smrg{ 397e88f27b3Smrg struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(push); 398e88f27b3Smrg struct nouveau_pushbuf_krec *krec = nvpb->krec; 399e88f27b3Smrg struct drm_nouveau_gem_pushbuf_bo *kref; 400e88f27b3Smrg struct nouveau_bufctx *bctx, *btmp; 401e88f27b3Smrg struct nouveau_bo *bo; 402e88f27b3Smrg int ret = 0, i; 403e88f27b3Smrg 404e88f27b3Smrg if (push->channel) { 405e88f27b3Smrg ret = pushbuf_submit(push, push->channel); 406e88f27b3Smrg } else { 407e88f27b3Smrg nouveau_pushbuf_data(push, NULL, 0, 0); 408e88f27b3Smrg krec->next = malloc(sizeof(*krec)); 409e88f27b3Smrg nvpb->krec = krec->next; 410e88f27b3Smrg } 411e88f27b3Smrg 412e88f27b3Smrg kref = krec->buffer; 413e88f27b3Smrg for (i = 0; i < krec->nr_buffer; i++, kref++) { 414e88f27b3Smrg bo = (void *)(unsigned long)kref->user_priv; 415e88f27b3Smrg cli_kref_set(push->client, bo, NULL, NULL); 416e88f27b3Smrg if (push->channel) 417e88f27b3Smrg nouveau_bo_ref(NULL, &bo); 418e88f27b3Smrg } 419e88f27b3Smrg 420e88f27b3Smrg krec = nvpb->krec; 421e88f27b3Smrg krec->vram_used = 0; 422e88f27b3Smrg krec->gart_used = 0; 423e88f27b3Smrg krec->nr_buffer = 0; 424e88f27b3Smrg krec->nr_reloc = 0; 425e88f27b3Smrg krec->nr_push = 0; 426e88f27b3Smrg 427e88f27b3Smrg DRMLISTFOREACHENTRYSAFE(bctx, btmp, &nvpb->bctx_list, head) { 428e88f27b3Smrg DRMLISTJOIN(&bctx->current, &bctx->pending); 429e88f27b3Smrg DRMINITLISTHEAD(&bctx->current); 430e88f27b3Smrg DRMLISTDELINIT(&bctx->head); 431e88f27b3Smrg } 432e88f27b3Smrg 433e88f27b3Smrg return ret; 434e88f27b3Smrg} 435e88f27b3Smrg 436e88f27b3Smrgstatic void 437e88f27b3Smrgpushbuf_refn_fail(struct nouveau_pushbuf *push, int sref, int srel) 438e88f27b3Smrg{ 439e88f27b3Smrg struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(push); 440e88f27b3Smrg struct nouveau_pushbuf_krec *krec = nvpb->krec; 441e88f27b3Smrg struct drm_nouveau_gem_pushbuf_bo *kref; 442e88f27b3Smrg 443e88f27b3Smrg kref = krec->buffer + sref; 444e88f27b3Smrg while (krec->nr_buffer-- > sref) { 445e88f27b3Smrg struct nouveau_bo *bo = (void *)(unsigned long)kref->user_priv; 446e88f27b3Smrg cli_kref_set(push->client, bo, NULL, NULL); 447e88f27b3Smrg nouveau_bo_ref(NULL, &bo); 448e88f27b3Smrg kref++; 449e88f27b3Smrg } 450e88f27b3Smrg krec->nr_buffer = sref; 451e88f27b3Smrg krec->nr_reloc = srel; 452e88f27b3Smrg} 453e88f27b3Smrg 454e88f27b3Smrgstatic int 455e88f27b3Smrgpushbuf_refn(struct nouveau_pushbuf *push, bool retry, 456e88f27b3Smrg struct nouveau_pushbuf_refn *refs, int nr) 457e88f27b3Smrg{ 458e88f27b3Smrg struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(push); 459e88f27b3Smrg struct nouveau_pushbuf_krec *krec = nvpb->krec; 460e88f27b3Smrg struct drm_nouveau_gem_pushbuf_bo *kref; 461e88f27b3Smrg int sref = krec->nr_buffer; 462e88f27b3Smrg int ret = 0, i; 463e88f27b3Smrg 464e88f27b3Smrg for (i = 0; i < nr; i++) { 465e88f27b3Smrg kref = pushbuf_kref(push, refs[i].bo, refs[i].flags); 466e88f27b3Smrg if (!kref) { 467e88f27b3Smrg ret = -ENOSPC; 468e88f27b3Smrg break; 469e88f27b3Smrg } 470e88f27b3Smrg } 471e88f27b3Smrg 472e88f27b3Smrg if (ret) { 473e88f27b3Smrg pushbuf_refn_fail(push, sref, krec->nr_reloc); 474e88f27b3Smrg if (retry) { 475e88f27b3Smrg pushbuf_flush(push); 476e88f27b3Smrg nouveau_pushbuf_space(push, 0, 0, 0); 477e88f27b3Smrg return pushbuf_refn(push, false, refs, nr); 478e88f27b3Smrg } 479e88f27b3Smrg } 480e88f27b3Smrg 481e88f27b3Smrg return ret; 482e88f27b3Smrg} 483e88f27b3Smrg 484e88f27b3Smrgstatic int 485e88f27b3Smrgpushbuf_validate(struct nouveau_pushbuf *push, bool retry) 486e88f27b3Smrg{ 487e88f27b3Smrg struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(push); 488e88f27b3Smrg struct nouveau_pushbuf_krec *krec = nvpb->krec; 489e88f27b3Smrg struct drm_nouveau_gem_pushbuf_bo *kref; 490e88f27b3Smrg struct nouveau_bufctx *bctx = push->bufctx; 491e88f27b3Smrg struct nouveau_bufref *bref; 492e88f27b3Smrg int relocs = bctx ? bctx->relocs * 2: 0; 493e88f27b3Smrg int sref, srel, ret; 494e88f27b3Smrg 495e88f27b3Smrg ret = nouveau_pushbuf_space(push, relocs, relocs, 0); 496e88f27b3Smrg if (ret || bctx == NULL) 497e88f27b3Smrg return ret; 498e88f27b3Smrg 499e88f27b3Smrg sref = krec->nr_buffer; 500e88f27b3Smrg srel = krec->nr_reloc; 501e88f27b3Smrg 502e88f27b3Smrg DRMLISTDEL(&bctx->head); 503e88f27b3Smrg DRMLISTADD(&bctx->head, &nvpb->bctx_list); 504e88f27b3Smrg 505e88f27b3Smrg DRMLISTFOREACHENTRY(bref, &bctx->pending, thead) { 506e88f27b3Smrg kref = pushbuf_kref(push, bref->bo, bref->flags); 507e88f27b3Smrg if (!kref) { 508e88f27b3Smrg ret = -ENOSPC; 509e88f27b3Smrg break; 510e88f27b3Smrg } 511e88f27b3Smrg 512e88f27b3Smrg if (bref->packet) { 513e88f27b3Smrg pushbuf_krel(push, bref->bo, bref->packet, 0, 0, 0); 514e88f27b3Smrg *push->cur++ = 0; 515e88f27b3Smrg pushbuf_krel(push, bref->bo, bref->data, bref->flags, 516e88f27b3Smrg bref->vor, bref->tor); 517e88f27b3Smrg *push->cur++ = 0; 518e88f27b3Smrg } 519e88f27b3Smrg } 520e88f27b3Smrg 521e88f27b3Smrg DRMLISTJOIN(&bctx->pending, &bctx->current); 522e88f27b3Smrg DRMINITLISTHEAD(&bctx->pending); 523e88f27b3Smrg 524e88f27b3Smrg if (ret) { 525e88f27b3Smrg pushbuf_refn_fail(push, sref, srel); 526e88f27b3Smrg if (retry) { 527e88f27b3Smrg pushbuf_flush(push); 528e88f27b3Smrg return pushbuf_validate(push, false); 529e88f27b3Smrg } 530e88f27b3Smrg } 531e88f27b3Smrg 532e88f27b3Smrg return ret; 533e88f27b3Smrg} 534e88f27b3Smrg 535e6188e58Smrgint 536e88f27b3Smrgnouveau_pushbuf_new(struct nouveau_client *client, struct nouveau_object *chan, 537e88f27b3Smrg int nr, uint32_t size, bool immediate, 538e88f27b3Smrg struct nouveau_pushbuf **ppush) 539e88f27b3Smrg{ 5403f012e29Smrg struct nouveau_drm *drm = nouveau_drm(&client->device->object); 541e88f27b3Smrg struct nouveau_fifo *fifo = chan->data; 542e88f27b3Smrg struct nouveau_pushbuf_priv *nvpb; 543e88f27b3Smrg struct nouveau_pushbuf *push; 544e88f27b3Smrg struct drm_nouveau_gem_pushbuf req = {}; 545e88f27b3Smrg int ret; 546e88f27b3Smrg 547e88f27b3Smrg if (chan->oclass != NOUVEAU_FIFO_CHANNEL_CLASS) 548e88f27b3Smrg return -EINVAL; 549e88f27b3Smrg 550e88f27b3Smrg /* nop pushbuf call, to get the current "return to main" sequence 551e88f27b3Smrg * we need to append to the pushbuf on early chipsets 552e88f27b3Smrg */ 553e88f27b3Smrg req.channel = fifo->channel; 554e88f27b3Smrg req.nr_push = 0; 5553f012e29Smrg ret = drmCommandWriteRead(drm->fd, DRM_NOUVEAU_GEM_PUSHBUF, 556e88f27b3Smrg &req, sizeof(req)); 557e88f27b3Smrg if (ret) 558e88f27b3Smrg return ret; 559e88f27b3Smrg 560e88f27b3Smrg nvpb = calloc(1, sizeof(*nvpb) + nr * sizeof(*nvpb->bos)); 561e88f27b3Smrg if (!nvpb) 562e88f27b3Smrg return -ENOMEM; 563e88f27b3Smrg 564e88f27b3Smrg#ifndef SIMULATE 565e88f27b3Smrg nvpb->suffix0 = req.suffix0; 566e88f27b3Smrg nvpb->suffix1 = req.suffix1; 567e88f27b3Smrg#else 568e88f27b3Smrg nvpb->suffix0 = 0xffffffff; 569e88f27b3Smrg nvpb->suffix1 = 0xffffffff; 570e88f27b3Smrg#endif 571e88f27b3Smrg nvpb->krec = calloc(1, sizeof(*nvpb->krec)); 572e88f27b3Smrg nvpb->list = nvpb->krec; 573e88f27b3Smrg if (!nvpb->krec) { 574e88f27b3Smrg free(nvpb); 575e88f27b3Smrg return -ENOMEM; 576e88f27b3Smrg } 577e88f27b3Smrg 578e88f27b3Smrg push = &nvpb->base; 579e88f27b3Smrg push->client = client; 580e88f27b3Smrg push->channel = immediate ? chan : NULL; 581e88f27b3Smrg push->flags = NOUVEAU_BO_RD; 582e88f27b3Smrg if (fifo->pushbuf & NOUVEAU_GEM_DOMAIN_GART) { 583e88f27b3Smrg push->flags |= NOUVEAU_BO_GART; 584e88f27b3Smrg nvpb->type = NOUVEAU_BO_GART; 585e88f27b3Smrg } else 586e88f27b3Smrg if (fifo->pushbuf & NOUVEAU_GEM_DOMAIN_VRAM) { 587e88f27b3Smrg push->flags |= NOUVEAU_BO_VRAM; 588e88f27b3Smrg nvpb->type = NOUVEAU_BO_VRAM; 589e88f27b3Smrg } 590e88f27b3Smrg nvpb->type |= NOUVEAU_BO_MAP; 591e88f27b3Smrg 592e88f27b3Smrg for (nvpb->bo_nr = 0; nvpb->bo_nr < nr; nvpb->bo_nr++) { 593e88f27b3Smrg ret = nouveau_bo_new(client->device, nvpb->type, 0, size, 594e88f27b3Smrg NULL, &nvpb->bos[nvpb->bo_nr]); 595e88f27b3Smrg if (ret) { 596e88f27b3Smrg nouveau_pushbuf_del(&push); 597e88f27b3Smrg return ret; 598e88f27b3Smrg } 599e88f27b3Smrg } 600e88f27b3Smrg 601e88f27b3Smrg DRMINITLISTHEAD(&nvpb->bctx_list); 602e88f27b3Smrg *ppush = push; 603e88f27b3Smrg return 0; 604e88f27b3Smrg} 605e88f27b3Smrg 606e6188e58Smrgvoid 607e88f27b3Smrgnouveau_pushbuf_del(struct nouveau_pushbuf **ppush) 608e88f27b3Smrg{ 609e88f27b3Smrg struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(*ppush); 610e88f27b3Smrg if (nvpb) { 611e88f27b3Smrg struct drm_nouveau_gem_pushbuf_bo *kref; 612e88f27b3Smrg struct nouveau_pushbuf_krec *krec; 613e88f27b3Smrg while ((krec = nvpb->list)) { 614e88f27b3Smrg kref = krec->buffer; 615e88f27b3Smrg while (krec->nr_buffer--) { 616e88f27b3Smrg unsigned long priv = kref++->user_priv; 617e88f27b3Smrg struct nouveau_bo *bo = (void *)priv; 618e88f27b3Smrg cli_kref_set(nvpb->base.client, bo, NULL, NULL); 619e88f27b3Smrg nouveau_bo_ref(NULL, &bo); 620e88f27b3Smrg } 621e88f27b3Smrg nvpb->list = krec->next; 622e88f27b3Smrg free(krec); 623e88f27b3Smrg } 624e88f27b3Smrg while (nvpb->bo_nr--) 625e88f27b3Smrg nouveau_bo_ref(NULL, &nvpb->bos[nvpb->bo_nr]); 626e88f27b3Smrg nouveau_bo_ref(NULL, &nvpb->bo); 627e88f27b3Smrg free(nvpb); 628e88f27b3Smrg } 629e88f27b3Smrg *ppush = NULL; 630e88f27b3Smrg} 631e88f27b3Smrg 632e6188e58Smrgstruct nouveau_bufctx * 633e88f27b3Smrgnouveau_pushbuf_bufctx(struct nouveau_pushbuf *push, struct nouveau_bufctx *ctx) 634e88f27b3Smrg{ 635e88f27b3Smrg struct nouveau_bufctx *prev = push->bufctx; 636e88f27b3Smrg push->bufctx = ctx; 637e88f27b3Smrg return prev; 638e88f27b3Smrg} 639e88f27b3Smrg 640e6188e58Smrgint 641e88f27b3Smrgnouveau_pushbuf_space(struct nouveau_pushbuf *push, 642e88f27b3Smrg uint32_t dwords, uint32_t relocs, uint32_t pushes) 643e88f27b3Smrg{ 644e88f27b3Smrg struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(push); 645e88f27b3Smrg struct nouveau_pushbuf_krec *krec = nvpb->krec; 646e88f27b3Smrg struct nouveau_client *client = push->client; 647e88f27b3Smrg struct nouveau_bo *bo = NULL; 648e88f27b3Smrg bool flushed = false; 649e88f27b3Smrg int ret = 0; 650e88f27b3Smrg 651e88f27b3Smrg /* switch to next buffer if insufficient space in the current one */ 652e88f27b3Smrg if (push->cur + dwords >= push->end) { 653e88f27b3Smrg if (nvpb->bo_next < nvpb->bo_nr) { 654e88f27b3Smrg nouveau_bo_ref(nvpb->bos[nvpb->bo_next++], &bo); 655e88f27b3Smrg if (nvpb->bo_next == nvpb->bo_nr && push->channel) 656e88f27b3Smrg nvpb->bo_next = 0; 657e88f27b3Smrg } else { 658e88f27b3Smrg ret = nouveau_bo_new(client->device, nvpb->type, 0, 659e88f27b3Smrg nvpb->bos[0]->size, NULL, &bo); 660e88f27b3Smrg if (ret) 661e88f27b3Smrg return ret; 662e88f27b3Smrg } 663e88f27b3Smrg } 664e88f27b3Smrg 665e88f27b3Smrg /* make sure there's always enough space to queue up the pending 666e88f27b3Smrg * data in the pushbuf proper 667e88f27b3Smrg */ 668e88f27b3Smrg pushes++; 669e88f27b3Smrg 670e88f27b3Smrg /* need to flush if we've run out of space on an immediate pushbuf, 671e88f27b3Smrg * if the new buffer won't fit, or if the kernel push/reloc limits 672e88f27b3Smrg * have been hit 673e88f27b3Smrg */ 674e88f27b3Smrg if ((bo && ( push->channel || 675e88f27b3Smrg !pushbuf_kref(push, bo, push->flags))) || 676e88f27b3Smrg krec->nr_reloc + relocs >= NOUVEAU_GEM_MAX_RELOCS || 677e88f27b3Smrg krec->nr_push + pushes >= NOUVEAU_GEM_MAX_PUSH) { 678e88f27b3Smrg if (nvpb->bo && krec->nr_buffer) 679e88f27b3Smrg pushbuf_flush(push); 680e88f27b3Smrg flushed = true; 681e88f27b3Smrg } 682e88f27b3Smrg 683e88f27b3Smrg /* if necessary, switch to new buffer */ 684e88f27b3Smrg if (bo) { 685e88f27b3Smrg ret = nouveau_bo_map(bo, NOUVEAU_BO_WR, push->client); 686e88f27b3Smrg if (ret) 687e88f27b3Smrg return ret; 688e88f27b3Smrg 689e88f27b3Smrg nouveau_pushbuf_data(push, NULL, 0, 0); 690e88f27b3Smrg nouveau_bo_ref(bo, &nvpb->bo); 691e88f27b3Smrg nouveau_bo_ref(NULL, &bo); 692e88f27b3Smrg 693e88f27b3Smrg nvpb->bgn = nvpb->bo->map; 694e88f27b3Smrg nvpb->ptr = nvpb->bgn; 695e88f27b3Smrg push->cur = nvpb->bgn; 696e88f27b3Smrg push->end = push->cur + (nvpb->bo->size / 4); 697e88f27b3Smrg push->end -= 2 + push->rsvd_kick; /* space for suffix */ 698e88f27b3Smrg } 699e88f27b3Smrg 700e88f27b3Smrg pushbuf_kref(push, nvpb->bo, push->flags); 701e88f27b3Smrg return flushed ? pushbuf_validate(push, false) : 0; 702e88f27b3Smrg} 703e88f27b3Smrg 704e6188e58Smrgvoid 705e88f27b3Smrgnouveau_pushbuf_data(struct nouveau_pushbuf *push, struct nouveau_bo *bo, 706e88f27b3Smrg uint64_t offset, uint64_t length) 707e88f27b3Smrg{ 708e88f27b3Smrg struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(push); 709e88f27b3Smrg struct nouveau_pushbuf_krec *krec = nvpb->krec; 710e88f27b3Smrg struct drm_nouveau_gem_pushbuf_push *kpsh; 711e88f27b3Smrg struct drm_nouveau_gem_pushbuf_bo *kref; 712e88f27b3Smrg 713e88f27b3Smrg if (bo != nvpb->bo && nvpb->bgn != push->cur) { 714e88f27b3Smrg if (nvpb->suffix0 || nvpb->suffix1) { 715e88f27b3Smrg *push->cur++ = nvpb->suffix0; 716e88f27b3Smrg *push->cur++ = nvpb->suffix1; 717e88f27b3Smrg } 718e88f27b3Smrg 719e88f27b3Smrg nouveau_pushbuf_data(push, nvpb->bo, 720e88f27b3Smrg (nvpb->bgn - nvpb->ptr) * 4, 721e88f27b3Smrg (push->cur - nvpb->bgn) * 4); 722e88f27b3Smrg nvpb->bgn = push->cur; 723e88f27b3Smrg } 724e88f27b3Smrg 725e88f27b3Smrg if (bo) { 726e88f27b3Smrg kref = cli_kref_get(push->client, bo); 727e6188e58Smrg assert(kref); 728e88f27b3Smrg kpsh = &krec->push[krec->nr_push++]; 729e88f27b3Smrg kpsh->bo_index = kref - krec->buffer; 730e88f27b3Smrg kpsh->offset = offset; 731e88f27b3Smrg kpsh->length = length; 732e88f27b3Smrg } 733e88f27b3Smrg} 734e88f27b3Smrg 735e6188e58Smrgint 736e88f27b3Smrgnouveau_pushbuf_refn(struct nouveau_pushbuf *push, 737e88f27b3Smrg struct nouveau_pushbuf_refn *refs, int nr) 738e88f27b3Smrg{ 739e88f27b3Smrg return pushbuf_refn(push, true, refs, nr); 740e88f27b3Smrg} 741e88f27b3Smrg 742e6188e58Smrgvoid 743e88f27b3Smrgnouveau_pushbuf_reloc(struct nouveau_pushbuf *push, struct nouveau_bo *bo, 744e88f27b3Smrg uint32_t data, uint32_t flags, uint32_t vor, uint32_t tor) 745e88f27b3Smrg{ 746e88f27b3Smrg *push->cur = pushbuf_krel(push, bo, data, flags, vor, tor); 747e88f27b3Smrg push->cur++; 748e88f27b3Smrg} 749e88f27b3Smrg 750e6188e58Smrgint 751e88f27b3Smrgnouveau_pushbuf_validate(struct nouveau_pushbuf *push) 752e88f27b3Smrg{ 753e88f27b3Smrg return pushbuf_validate(push, true); 754e88f27b3Smrg} 755e88f27b3Smrg 756e6188e58Smrguint32_t 757e88f27b3Smrgnouveau_pushbuf_refd(struct nouveau_pushbuf *push, struct nouveau_bo *bo) 758e88f27b3Smrg{ 759e88f27b3Smrg struct drm_nouveau_gem_pushbuf_bo *kref; 760e88f27b3Smrg uint32_t flags = 0; 761e88f27b3Smrg 762e88f27b3Smrg if (cli_push_get(push->client, bo) == push) { 763e88f27b3Smrg kref = cli_kref_get(push->client, bo); 764e6188e58Smrg assert(kref); 765e88f27b3Smrg if (kref->read_domains) 766e88f27b3Smrg flags |= NOUVEAU_BO_RD; 767e88f27b3Smrg if (kref->write_domains) 768e88f27b3Smrg flags |= NOUVEAU_BO_WR; 769e88f27b3Smrg } 770e88f27b3Smrg 771e88f27b3Smrg return flags; 772e88f27b3Smrg} 773e88f27b3Smrg 774e6188e58Smrgint 775e88f27b3Smrgnouveau_pushbuf_kick(struct nouveau_pushbuf *push, struct nouveau_object *chan) 776e88f27b3Smrg{ 777e88f27b3Smrg if (!push->channel) 778e88f27b3Smrg return pushbuf_submit(push, chan); 779e88f27b3Smrg pushbuf_flush(push); 780e88f27b3Smrg return pushbuf_validate(push, false); 781e88f27b3Smrg} 782