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