1235783Skib/* 2235783Skib * Copyright �� 2008,2010 Intel Corporation 3235783Skib * 4235783Skib * Permission is hereby granted, free of charge, to any person obtaining a 5235783Skib * copy of this software and associated documentation files (the "Software"), 6235783Skib * to deal in the Software without restriction, including without limitation 7235783Skib * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8235783Skib * and/or sell copies of the Software, and to permit persons to whom the 9235783Skib * Software is furnished to do so, subject to the following conditions: 10235783Skib * 11235783Skib * The above copyright notice and this permission notice (including the next 12235783Skib * paragraph) shall be included in all copies or substantial portions of the 13235783Skib * Software. 14235783Skib * 15235783Skib * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16235783Skib * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17235783Skib * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18235783Skib * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19235783Skib * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20235783Skib * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21235783Skib * IN THE SOFTWARE. 22235783Skib * 23235783Skib * Authors: 24235783Skib * Eric Anholt <eric@anholt.net> 25235783Skib * Chris Wilson <chris@chris-wilson.co.uk> 26235783Skib * 27235783Skib */ 28235783Skib 29235783Skib#include <sys/cdefs.h> 30235783Skib__FBSDID("$FreeBSD: releng/10.3/sys/dev/drm2/i915/i915_gem_execbuffer.c 290454 2015-11-06 16:48:33Z jhb $"); 31235783Skib 32235783Skib#include <dev/drm2/drmP.h> 33235783Skib#include <dev/drm2/drm.h> 34235783Skib#include <dev/drm2/i915/i915_drm.h> 35235783Skib#include <dev/drm2/i915/i915_drv.h> 36235783Skib#include <dev/drm2/i915/intel_drv.h> 37235783Skib#include <sys/limits.h> 38235783Skib#include <sys/sf_buf.h> 39235783Skib 40235783Skibstruct change_domains { 41235783Skib uint32_t invalidate_domains; 42235783Skib uint32_t flush_domains; 43235783Skib uint32_t flush_rings; 44235783Skib uint32_t flips; 45235783Skib}; 46235783Skib 47235783Skib/* 48235783Skib * Set the next domain for the specified object. This 49235783Skib * may not actually perform the necessary flushing/invaliding though, 50235783Skib * as that may want to be batched with other set_domain operations 51235783Skib * 52235783Skib * This is (we hope) the only really tricky part of gem. The goal 53235783Skib * is fairly simple -- track which caches hold bits of the object 54235783Skib * and make sure they remain coherent. A few concrete examples may 55235783Skib * help to explain how it works. For shorthand, we use the notation 56235783Skib * (read_domains, write_domain), e.g. (CPU, CPU) to indicate the 57235783Skib * a pair of read and write domain masks. 58235783Skib * 59235783Skib * Case 1: the batch buffer 60235783Skib * 61235783Skib * 1. Allocated 62235783Skib * 2. Written by CPU 63235783Skib * 3. Mapped to GTT 64235783Skib * 4. Read by GPU 65235783Skib * 5. Unmapped from GTT 66235783Skib * 6. Freed 67235783Skib * 68235783Skib * Let's take these a step at a time 69235783Skib * 70235783Skib * 1. Allocated 71235783Skib * Pages allocated from the kernel may still have 72235783Skib * cache contents, so we set them to (CPU, CPU) always. 73235783Skib * 2. Written by CPU (using pwrite) 74235783Skib * The pwrite function calls set_domain (CPU, CPU) and 75235783Skib * this function does nothing (as nothing changes) 76235783Skib * 3. Mapped by GTT 77235783Skib * This function asserts that the object is not 78235783Skib * currently in any GPU-based read or write domains 79235783Skib * 4. Read by GPU 80235783Skib * i915_gem_execbuffer calls set_domain (COMMAND, 0). 81235783Skib * As write_domain is zero, this function adds in the 82235783Skib * current read domains (CPU+COMMAND, 0). 83235783Skib * flush_domains is set to CPU. 84235783Skib * invalidate_domains is set to COMMAND 85235783Skib * clflush is run to get data out of the CPU caches 86235783Skib * then i915_dev_set_domain calls i915_gem_flush to 87235783Skib * emit an MI_FLUSH and drm_agp_chipset_flush 88235783Skib * 5. Unmapped from GTT 89235783Skib * i915_gem_object_unbind calls set_domain (CPU, CPU) 90235783Skib * flush_domains and invalidate_domains end up both zero 91235783Skib * so no flushing/invalidating happens 92235783Skib * 6. Freed 93235783Skib * yay, done 94235783Skib * 95235783Skib * Case 2: The shared render buffer 96235783Skib * 97235783Skib * 1. Allocated 98235783Skib * 2. Mapped to GTT 99235783Skib * 3. Read/written by GPU 100235783Skib * 4. set_domain to (CPU,CPU) 101235783Skib * 5. Read/written by CPU 102235783Skib * 6. Read/written by GPU 103235783Skib * 104235783Skib * 1. Allocated 105235783Skib * Same as last example, (CPU, CPU) 106235783Skib * 2. Mapped to GTT 107235783Skib * Nothing changes (assertions find that it is not in the GPU) 108235783Skib * 3. Read/written by GPU 109235783Skib * execbuffer calls set_domain (RENDER, RENDER) 110235783Skib * flush_domains gets CPU 111235783Skib * invalidate_domains gets GPU 112235783Skib * clflush (obj) 113235783Skib * MI_FLUSH and drm_agp_chipset_flush 114235783Skib * 4. set_domain (CPU, CPU) 115235783Skib * flush_domains gets GPU 116235783Skib * invalidate_domains gets CPU 117235783Skib * wait_rendering (obj) to make sure all drawing is complete. 118235783Skib * This will include an MI_FLUSH to get the data from GPU 119235783Skib * to memory 120235783Skib * clflush (obj) to invalidate the CPU cache 121235783Skib * Another MI_FLUSH in i915_gem_flush (eliminate this somehow?) 122235783Skib * 5. Read/written by CPU 123235783Skib * cache lines are loaded and dirtied 124235783Skib * 6. Read written by GPU 125235783Skib * Same as last GPU access 126235783Skib * 127235783Skib * Case 3: The constant buffer 128235783Skib * 129235783Skib * 1. Allocated 130235783Skib * 2. Written by CPU 131235783Skib * 3. Read by GPU 132235783Skib * 4. Updated (written) by CPU again 133235783Skib * 5. Read by GPU 134235783Skib * 135235783Skib * 1. Allocated 136235783Skib * (CPU, CPU) 137235783Skib * 2. Written by CPU 138235783Skib * (CPU, CPU) 139235783Skib * 3. Read by GPU 140235783Skib * (CPU+RENDER, 0) 141235783Skib * flush_domains = CPU 142235783Skib * invalidate_domains = RENDER 143235783Skib * clflush (obj) 144235783Skib * MI_FLUSH 145235783Skib * drm_agp_chipset_flush 146235783Skib * 4. Updated (written) by CPU again 147235783Skib * (CPU, CPU) 148235783Skib * flush_domains = 0 (no previous write domain) 149235783Skib * invalidate_domains = 0 (no new read domains) 150235783Skib * 5. Read by GPU 151235783Skib * (CPU+RENDER, 0) 152235783Skib * flush_domains = CPU 153235783Skib * invalidate_domains = RENDER 154235783Skib * clflush (obj) 155235783Skib * MI_FLUSH 156235783Skib * drm_agp_chipset_flush 157235783Skib */ 158235783Skibstatic void 159235783Skibi915_gem_object_set_to_gpu_domain(struct drm_i915_gem_object *obj, 160235783Skib struct intel_ring_buffer *ring, 161235783Skib struct change_domains *cd) 162235783Skib{ 163235783Skib uint32_t invalidate_domains = 0, flush_domains = 0; 164235783Skib 165235783Skib /* 166235783Skib * If the object isn't moving to a new write domain, 167235783Skib * let the object stay in multiple read domains 168235783Skib */ 169235783Skib if (obj->base.pending_write_domain == 0) 170235783Skib obj->base.pending_read_domains |= obj->base.read_domains; 171235783Skib 172235783Skib /* 173235783Skib * Flush the current write domain if 174235783Skib * the new read domains don't match. Invalidate 175235783Skib * any read domains which differ from the old 176235783Skib * write domain 177235783Skib */ 178235783Skib if (obj->base.write_domain && 179235783Skib (((obj->base.write_domain != obj->base.pending_read_domains || 180235783Skib obj->ring != ring)) || 181235783Skib (obj->fenced_gpu_access && !obj->pending_fenced_gpu_access))) { 182235783Skib flush_domains |= obj->base.write_domain; 183235783Skib invalidate_domains |= 184235783Skib obj->base.pending_read_domains & ~obj->base.write_domain; 185235783Skib } 186235783Skib /* 187235783Skib * Invalidate any read caches which may have 188235783Skib * stale data. That is, any new read domains. 189235783Skib */ 190235783Skib invalidate_domains |= obj->base.pending_read_domains & ~obj->base.read_domains; 191235783Skib if ((flush_domains | invalidate_domains) & I915_GEM_DOMAIN_CPU) 192235783Skib i915_gem_clflush_object(obj); 193235783Skib 194235783Skib if (obj->base.pending_write_domain) 195255013Sjkim cd->flips |= atomic_load_acq_int(&obj->pending_flip); 196235783Skib 197235783Skib /* The actual obj->write_domain will be updated with 198235783Skib * pending_write_domain after we emit the accumulated flush for all 199235783Skib * of our domain changes in execbuffers (which clears objects' 200235783Skib * write_domains). So if we have a current write domain that we 201235783Skib * aren't changing, set pending_write_domain to that. 202235783Skib */ 203235783Skib if (flush_domains == 0 && obj->base.pending_write_domain == 0) 204235783Skib obj->base.pending_write_domain = obj->base.write_domain; 205235783Skib 206235783Skib cd->invalidate_domains |= invalidate_domains; 207235783Skib cd->flush_domains |= flush_domains; 208235783Skib if (flush_domains & I915_GEM_GPU_DOMAINS) 209235783Skib cd->flush_rings |= intel_ring_flag(obj->ring); 210235783Skib if (invalidate_domains & I915_GEM_GPU_DOMAINS) 211235783Skib cd->flush_rings |= intel_ring_flag(ring); 212235783Skib} 213235783Skib 214235783Skibstruct eb_objects { 215235783Skib u_long hashmask; 216235783Skib LIST_HEAD(, drm_i915_gem_object) *buckets; 217235783Skib}; 218235783Skib 219235783Skibstatic struct eb_objects * 220235783Skibeb_create(int size) 221235783Skib{ 222235783Skib struct eb_objects *eb; 223235783Skib 224235783Skib eb = malloc(sizeof(*eb), DRM_I915_GEM, M_WAITOK | M_ZERO); 225235783Skib eb->buckets = hashinit(size, DRM_I915_GEM, &eb->hashmask); 226235783Skib return (eb); 227235783Skib} 228235783Skib 229235783Skibstatic void 230235783Skibeb_reset(struct eb_objects *eb) 231235783Skib{ 232235783Skib int i; 233235783Skib 234235783Skib for (i = 0; i <= eb->hashmask; i++) 235235783Skib LIST_INIT(&eb->buckets[i]); 236235783Skib} 237235783Skib 238235783Skibstatic void 239235783Skibeb_add_object(struct eb_objects *eb, struct drm_i915_gem_object *obj) 240235783Skib{ 241235783Skib 242235783Skib LIST_INSERT_HEAD(&eb->buckets[obj->exec_handle & eb->hashmask], 243235783Skib obj, exec_node); 244235783Skib} 245235783Skib 246235783Skibstatic struct drm_i915_gem_object * 247235783Skibeb_get_object(struct eb_objects *eb, unsigned long handle) 248235783Skib{ 249235783Skib struct drm_i915_gem_object *obj; 250235783Skib 251235783Skib LIST_FOREACH(obj, &eb->buckets[handle & eb->hashmask], exec_node) { 252235783Skib if (obj->exec_handle == handle) 253235783Skib return (obj); 254235783Skib } 255235783Skib return (NULL); 256235783Skib} 257235783Skib 258235783Skibstatic void 259235783Skibeb_destroy(struct eb_objects *eb) 260235783Skib{ 261235783Skib 262235783Skib free(eb->buckets, DRM_I915_GEM); 263235783Skib free(eb, DRM_I915_GEM); 264235783Skib} 265235783Skib 266280369Skibstatic inline int use_cpu_reloc(struct drm_i915_gem_object *obj) 267280369Skib{ 268280369Skib return (obj->base.write_domain == I915_GEM_DOMAIN_CPU || 269280369Skib obj->cache_level != I915_CACHE_NONE); 270280369Skib} 271280369Skib 272235783Skibstatic int 273235783Skibi915_gem_execbuffer_relocate_entry(struct drm_i915_gem_object *obj, 274235783Skib struct eb_objects *eb, 275235783Skib struct drm_i915_gem_relocation_entry *reloc) 276235783Skib{ 277235783Skib struct drm_device *dev = obj->base.dev; 278235783Skib struct drm_gem_object *target_obj; 279280369Skib struct drm_i915_gem_object *target_i915_obj; 280235783Skib uint32_t target_offset; 281235783Skib int ret = -EINVAL; 282235783Skib 283235783Skib /* we've already hold a reference to all valid objects */ 284235783Skib target_obj = &eb_get_object(eb, reloc->target_handle)->base; 285235783Skib if (unlikely(target_obj == NULL)) 286235783Skib return -ENOENT; 287235783Skib 288280369Skib target_i915_obj = to_intel_bo(target_obj); 289280369Skib target_offset = target_i915_obj->gtt_offset; 290235783Skib 291235783Skib#if WATCH_RELOC 292235783Skib DRM_INFO("%s: obj %p offset %08x target %d " 293235783Skib "read %08x write %08x gtt %08x " 294235783Skib "presumed %08x delta %08x\n", 295235783Skib __func__, 296235783Skib obj, 297235783Skib (int) reloc->offset, 298235783Skib (int) reloc->target_handle, 299235783Skib (int) reloc->read_domains, 300235783Skib (int) reloc->write_domain, 301235783Skib (int) target_offset, 302235783Skib (int) reloc->presumed_offset, 303235783Skib reloc->delta); 304235783Skib#endif 305235783Skib 306235783Skib /* The target buffer should have appeared before us in the 307235783Skib * exec_object list, so it should have a GTT space bound by now. 308235783Skib */ 309235783Skib if (unlikely(target_offset == 0)) { 310235783Skib DRM_DEBUG("No GTT space found for object %d\n", 311235783Skib reloc->target_handle); 312235783Skib return ret; 313235783Skib } 314235783Skib 315235783Skib /* Validate that the target is in a valid r/w GPU domain */ 316235783Skib if (unlikely(reloc->write_domain & (reloc->write_domain - 1))) { 317235783Skib DRM_DEBUG("reloc with multiple write domains: " 318235783Skib "obj %p target %d offset %d " 319235783Skib "read %08x write %08x", 320235783Skib obj, reloc->target_handle, 321235783Skib (int) reloc->offset, 322235783Skib reloc->read_domains, 323235783Skib reloc->write_domain); 324235783Skib return ret; 325235783Skib } 326235783Skib if (unlikely((reloc->write_domain | reloc->read_domains) 327235783Skib & ~I915_GEM_GPU_DOMAINS)) { 328235783Skib DRM_DEBUG("reloc with read/write non-GPU domains: " 329235783Skib "obj %p target %d offset %d " 330235783Skib "read %08x write %08x", 331235783Skib obj, reloc->target_handle, 332235783Skib (int) reloc->offset, 333235783Skib reloc->read_domains, 334235783Skib reloc->write_domain); 335235783Skib return ret; 336235783Skib } 337235783Skib if (unlikely(reloc->write_domain && target_obj->pending_write_domain && 338235783Skib reloc->write_domain != target_obj->pending_write_domain)) { 339235783Skib DRM_DEBUG("Write domain conflict: " 340235783Skib "obj %p target %d offset %d " 341235783Skib "new %08x old %08x\n", 342235783Skib obj, reloc->target_handle, 343235783Skib (int) reloc->offset, 344235783Skib reloc->write_domain, 345235783Skib target_obj->pending_write_domain); 346235783Skib return ret; 347235783Skib } 348235783Skib 349235783Skib target_obj->pending_read_domains |= reloc->read_domains; 350235783Skib target_obj->pending_write_domain |= reloc->write_domain; 351235783Skib 352235783Skib /* If the relocation already has the right value in it, no 353235783Skib * more work needs to be done. 354235783Skib */ 355235783Skib if (target_offset == reloc->presumed_offset) 356235783Skib return 0; 357235783Skib 358235783Skib /* Check that the relocation address is valid... */ 359235783Skib if (unlikely(reloc->offset > obj->base.size - 4)) { 360235783Skib DRM_DEBUG("Relocation beyond object bounds: " 361235783Skib "obj %p target %d offset %d size %d.\n", 362235783Skib obj, reloc->target_handle, 363235783Skib (int) reloc->offset, 364235783Skib (int) obj->base.size); 365235783Skib return ret; 366235783Skib } 367235783Skib if (unlikely(reloc->offset & 3)) { 368235783Skib DRM_DEBUG("Relocation not 4-byte aligned: " 369235783Skib "obj %p target %d offset %d.\n", 370235783Skib obj, reloc->target_handle, 371235783Skib (int) reloc->offset); 372235783Skib return ret; 373235783Skib } 374235783Skib 375280369Skib /* We can't wait for rendering with pagefaults disabled */ 376280369Skib if (obj->active && (curthread->td_pflags & TDP_NOFAULTING) != 0) 377280369Skib return (-EFAULT); 378280369Skib 379235783Skib reloc->delta += target_offset; 380280369Skib if (use_cpu_reloc(obj)) { 381235783Skib uint32_t page_offset = reloc->offset & PAGE_MASK; 382235783Skib char *vaddr; 383235783Skib struct sf_buf *sf; 384235783Skib 385280369Skib ret = i915_gem_object_set_to_cpu_domain(obj, 1); 386280369Skib if (ret) 387280369Skib return ret; 388280369Skib 389235783Skib sf = sf_buf_alloc(obj->pages[OFF_TO_IDX(reloc->offset)], 390235783Skib SFB_NOWAIT); 391235783Skib if (sf == NULL) 392235783Skib return (-ENOMEM); 393235783Skib vaddr = (void *)sf_buf_kva(sf); 394235783Skib *(uint32_t *)(vaddr + page_offset) = reloc->delta; 395235783Skib sf_buf_free(sf); 396235783Skib } else { 397235783Skib uint32_t *reloc_entry; 398235783Skib char *reloc_page; 399235783Skib 400280369Skib ret = i915_gem_object_set_to_gtt_domain(obj, true); 401235783Skib if (ret) 402235783Skib return ret; 403235783Skib 404280369Skib ret = i915_gem_object_put_fence(obj); 405280369Skib if (ret) 406280369Skib return ret; 407280369Skib 408235783Skib /* 409235783Skib * Map the page containing the relocation we're going 410235783Skib * to perform. 411235783Skib */ 412235783Skib reloc->offset += obj->gtt_offset; 413235783Skib reloc_page = pmap_mapdev_attr(dev->agp->base + (reloc->offset & 414235783Skib ~PAGE_MASK), PAGE_SIZE, PAT_WRITE_COMBINING); 415235783Skib reloc_entry = (uint32_t *)(reloc_page + (reloc->offset & 416235783Skib PAGE_MASK)); 417235783Skib *(volatile uint32_t *)reloc_entry = reloc->delta; 418235783Skib pmap_unmapdev((vm_offset_t)reloc_page, PAGE_SIZE); 419235783Skib } 420235783Skib 421280369Skib /* Sandybridge PPGTT errata: We need a global gtt mapping for MI and 422280369Skib * pipe_control writes because the gpu doesn't properly redirect them 423280369Skib * through the ppgtt for non_secure batchbuffers. */ 424280369Skib if (unlikely(IS_GEN6(dev) && 425280369Skib reloc->write_domain == I915_GEM_DOMAIN_INSTRUCTION && 426280369Skib !target_i915_obj->has_global_gtt_mapping)) { 427280369Skib i915_gem_gtt_bind_object(target_i915_obj, 428280369Skib target_i915_obj->cache_level); 429280369Skib } 430280369Skib 431235783Skib /* and update the user's relocation entry */ 432235783Skib reloc->presumed_offset = target_offset; 433235783Skib 434235783Skib return 0; 435235783Skib} 436235783Skib 437235783Skibstatic int 438235783Skibi915_gem_execbuffer_relocate_object(struct drm_i915_gem_object *obj, 439235783Skib struct eb_objects *eb) 440235783Skib{ 441280369Skib#define N_RELOC(x) ((x) / sizeof(struct drm_i915_gem_relocation_entry)) 442280369Skib struct drm_i915_gem_relocation_entry stack_reloc[N_RELOC(512)]; 443235783Skib struct drm_i915_gem_relocation_entry *user_relocs; 444235783Skib struct drm_i915_gem_exec_object2 *entry = obj->exec_entry; 445280369Skib int remain, ret; 446235783Skib 447235783Skib user_relocs = (void *)(uintptr_t)entry->relocs_ptr; 448280369Skib remain = entry->relocation_count; 449280369Skib while (remain) { 450280369Skib struct drm_i915_gem_relocation_entry *r = stack_reloc; 451280369Skib int count = remain; 452280369Skib if (count > DRM_ARRAY_SIZE(stack_reloc)) 453280369Skib count = DRM_ARRAY_SIZE(stack_reloc); 454280369Skib remain -= count; 455235783Skib 456280369Skib ret = -copyin_nofault(user_relocs, r, count*sizeof(r[0])); 457235783Skib if (ret != 0) 458235783Skib return (ret); 459235783Skib 460280369Skib do { 461280369Skib u64 offset = r->presumed_offset; 462280369Skib 463280369Skib ret = i915_gem_execbuffer_relocate_entry(obj, eb, r); 464280369Skib if (ret) 465280369Skib return ret; 466280369Skib 467280369Skib if (r->presumed_offset != offset && 468280369Skib copyout_nofault(&r->presumed_offset, 469280369Skib &user_relocs->presumed_offset, 470280369Skib sizeof(r->presumed_offset))) { 471280369Skib return -EFAULT; 472280369Skib } 473280369Skib 474280369Skib user_relocs++; 475280369Skib r++; 476280369Skib } while (--count); 477235783Skib } 478280369Skib#undef N_RELOC 479235783Skib return (0); 480235783Skib} 481235783Skib 482235783Skibstatic int 483235783Skibi915_gem_execbuffer_relocate_object_slow(struct drm_i915_gem_object *obj, 484235783Skib struct eb_objects *eb, struct drm_i915_gem_relocation_entry *relocs) 485235783Skib{ 486235783Skib const struct drm_i915_gem_exec_object2 *entry = obj->exec_entry; 487235783Skib int i, ret; 488235783Skib 489235783Skib for (i = 0; i < entry->relocation_count; i++) { 490235783Skib ret = i915_gem_execbuffer_relocate_entry(obj, eb, &relocs[i]); 491235783Skib if (ret) 492235783Skib return ret; 493235783Skib } 494235783Skib 495235783Skib return 0; 496235783Skib} 497235783Skib 498235783Skibstatic int 499235783Skibi915_gem_execbuffer_relocate(struct drm_device *dev, 500235783Skib struct eb_objects *eb, 501235783Skib struct list_head *objects) 502235783Skib{ 503235783Skib struct drm_i915_gem_object *obj; 504235783Skib int ret, pflags; 505235783Skib 506235783Skib /* Try to move as many of the relocation targets off the active list 507235783Skib * to avoid unnecessary fallbacks to the slow path, as we cannot wait 508235783Skib * for the retirement with pagefaults disabled. 509235783Skib */ 510235783Skib i915_gem_retire_requests(dev); 511235783Skib 512235783Skib ret = 0; 513235783Skib pflags = vm_fault_disable_pagefaults(); 514235783Skib /* This is the fast path and we cannot handle a pagefault whilst 515235783Skib * holding the device lock lest the user pass in the relocations 516235783Skib * contained within a mmaped bo. For in such a case we, the page 517235783Skib * fault handler would call i915_gem_fault() and we would try to 518235783Skib * acquire the device lock again. Obviously this is bad. 519235783Skib */ 520235783Skib 521235783Skib list_for_each_entry(obj, objects, exec_list) { 522235783Skib ret = i915_gem_execbuffer_relocate_object(obj, eb); 523235783Skib if (ret != 0) 524235783Skib break; 525235783Skib } 526235783Skib vm_fault_enable_pagefaults(pflags); 527235783Skib return (ret); 528235783Skib} 529235783Skib 530235783Skib#define __EXEC_OBJECT_HAS_FENCE (1<<31) 531235783Skib 532235783Skibstatic int 533280369Skibneed_reloc_mappable(struct drm_i915_gem_object *obj) 534280369Skib{ 535280369Skib struct drm_i915_gem_exec_object2 *entry = obj->exec_entry; 536280369Skib return entry->relocation_count && !use_cpu_reloc(obj); 537280369Skib} 538280369Skib 539280369Skibstatic int 540235783Skibpin_and_fence_object(struct drm_i915_gem_object *obj, 541235783Skib struct intel_ring_buffer *ring) 542235783Skib{ 543235783Skib struct drm_i915_gem_exec_object2 *entry = obj->exec_entry; 544235783Skib bool has_fenced_gpu_access = INTEL_INFO(ring->dev)->gen < 4; 545235783Skib bool need_fence, need_mappable; 546235783Skib int ret; 547235783Skib 548235783Skib need_fence = 549235783Skib has_fenced_gpu_access && 550235783Skib entry->flags & EXEC_OBJECT_NEEDS_FENCE && 551235783Skib obj->tiling_mode != I915_TILING_NONE; 552280369Skib need_mappable = need_fence || need_reloc_mappable(obj); 553235783Skib 554235783Skib ret = i915_gem_object_pin(obj, entry->alignment, need_mappable); 555235783Skib if (ret) 556235783Skib return ret; 557235783Skib 558235783Skib if (has_fenced_gpu_access) { 559235783Skib if (entry->flags & EXEC_OBJECT_NEEDS_FENCE) { 560280369Skib ret = i915_gem_object_get_fence(obj); 561280369Skib if (ret) 562280369Skib goto err_unpin; 563235783Skib 564280369Skib if (i915_gem_object_pin_fence(obj)) 565235783Skib entry->flags |= __EXEC_OBJECT_HAS_FENCE; 566280369Skib 567235783Skib obj->pending_fenced_gpu_access = true; 568235783Skib } 569235783Skib } 570235783Skib 571235783Skib entry->offset = obj->gtt_offset; 572235783Skib return 0; 573235783Skib 574235783Skiberr_unpin: 575235783Skib i915_gem_object_unpin(obj); 576235783Skib return ret; 577235783Skib} 578235783Skib 579235783Skibstatic int 580235783Skibi915_gem_execbuffer_reserve(struct intel_ring_buffer *ring, 581235783Skib struct drm_file *file, 582235783Skib struct list_head *objects) 583235783Skib{ 584235783Skib drm_i915_private_t *dev_priv; 585235783Skib struct drm_i915_gem_object *obj; 586235783Skib int ret, retry; 587235783Skib bool has_fenced_gpu_access = INTEL_INFO(ring->dev)->gen < 4; 588235783Skib struct list_head ordered_objects; 589235783Skib 590235783Skib dev_priv = ring->dev->dev_private; 591235783Skib INIT_LIST_HEAD(&ordered_objects); 592235783Skib while (!list_empty(objects)) { 593235783Skib struct drm_i915_gem_exec_object2 *entry; 594235783Skib bool need_fence, need_mappable; 595235783Skib 596235783Skib obj = list_first_entry(objects, 597235783Skib struct drm_i915_gem_object, 598235783Skib exec_list); 599235783Skib entry = obj->exec_entry; 600235783Skib 601235783Skib need_fence = 602235783Skib has_fenced_gpu_access && 603235783Skib entry->flags & EXEC_OBJECT_NEEDS_FENCE && 604235783Skib obj->tiling_mode != I915_TILING_NONE; 605280369Skib need_mappable = need_fence || need_reloc_mappable(obj); 606235783Skib 607235783Skib if (need_mappable) 608235783Skib list_move(&obj->exec_list, &ordered_objects); 609235783Skib else 610235783Skib list_move_tail(&obj->exec_list, &ordered_objects); 611235783Skib 612235783Skib obj->base.pending_read_domains = 0; 613235783Skib obj->base.pending_write_domain = 0; 614235783Skib } 615235783Skib list_splice(&ordered_objects, objects); 616235783Skib 617235783Skib /* Attempt to pin all of the buffers into the GTT. 618235783Skib * This is done in 3 phases: 619235783Skib * 620235783Skib * 1a. Unbind all objects that do not match the GTT constraints for 621235783Skib * the execbuffer (fenceable, mappable, alignment etc). 622235783Skib * 1b. Increment pin count for already bound objects and obtain 623235783Skib * a fence register if required. 624235783Skib * 2. Bind new objects. 625235783Skib * 3. Decrement pin count. 626235783Skib * 627235783Skib * This avoid unnecessary unbinding of later objects in order to makr 628235783Skib * room for the earlier objects *unless* we need to defragment. 629235783Skib */ 630235783Skib retry = 0; 631235783Skib do { 632235783Skib ret = 0; 633235783Skib 634235783Skib /* Unbind any ill-fitting objects or pin. */ 635235783Skib list_for_each_entry(obj, objects, exec_list) { 636235783Skib struct drm_i915_gem_exec_object2 *entry = obj->exec_entry; 637235783Skib bool need_fence, need_mappable; 638235783Skib 639235783Skib if (!obj->gtt_space) 640235783Skib continue; 641235783Skib 642235783Skib need_fence = 643235783Skib has_fenced_gpu_access && 644235783Skib entry->flags & EXEC_OBJECT_NEEDS_FENCE && 645235783Skib obj->tiling_mode != I915_TILING_NONE; 646280369Skib need_mappable = need_fence || need_reloc_mappable(obj); 647235783Skib 648235783Skib if ((entry->alignment && obj->gtt_offset & (entry->alignment - 1)) || 649235783Skib (need_mappable && !obj->map_and_fenceable)) 650235783Skib ret = i915_gem_object_unbind(obj); 651235783Skib else 652235783Skib ret = pin_and_fence_object(obj, ring); 653235783Skib if (ret) 654235783Skib goto err; 655235783Skib } 656235783Skib 657235783Skib /* Bind fresh objects */ 658235783Skib list_for_each_entry(obj, objects, exec_list) { 659235783Skib if (obj->gtt_space) 660235783Skib continue; 661235783Skib 662235783Skib ret = pin_and_fence_object(obj, ring); 663235783Skib if (ret) { 664235783Skib int ret_ignore; 665235783Skib 666235783Skib /* This can potentially raise a harmless 667235783Skib * -EINVAL if we failed to bind in the above 668235783Skib * call. It cannot raise -EINTR since we know 669235783Skib * that the bo is freshly bound and so will 670235783Skib * not need to be flushed or waited upon. 671235783Skib */ 672235783Skib ret_ignore = i915_gem_object_unbind(obj); 673235783Skib (void)ret_ignore; 674235783Skib if (obj->gtt_space != NULL) 675235783Skib printf("%s: gtt_space\n", __func__); 676235783Skib break; 677235783Skib } 678235783Skib } 679235783Skib 680235783Skib /* Decrement pin count for bound objects */ 681235783Skib list_for_each_entry(obj, objects, exec_list) { 682235783Skib struct drm_i915_gem_exec_object2 *entry; 683235783Skib 684235783Skib if (!obj->gtt_space) 685235783Skib continue; 686235783Skib 687235783Skib entry = obj->exec_entry; 688235783Skib if (entry->flags & __EXEC_OBJECT_HAS_FENCE) { 689235783Skib i915_gem_object_unpin_fence(obj); 690235783Skib entry->flags &= ~__EXEC_OBJECT_HAS_FENCE; 691235783Skib } 692235783Skib 693235783Skib i915_gem_object_unpin(obj); 694235783Skib 695235783Skib /* ... and ensure ppgtt mapping exist if needed. */ 696235783Skib if (dev_priv->mm.aliasing_ppgtt && !obj->has_aliasing_ppgtt_mapping) { 697235783Skib i915_ppgtt_bind_object(dev_priv->mm.aliasing_ppgtt, 698235783Skib obj, obj->cache_level); 699235783Skib 700235783Skib obj->has_aliasing_ppgtt_mapping = 1; 701235783Skib } 702235783Skib } 703235783Skib 704235783Skib if (ret != -ENOSPC || retry > 1) 705235783Skib return ret; 706235783Skib 707235783Skib /* First attempt, just clear anything that is purgeable. 708235783Skib * Second attempt, clear the entire GTT. 709235783Skib */ 710235783Skib ret = i915_gem_evict_everything(ring->dev, retry == 0); 711235783Skib if (ret) 712235783Skib return ret; 713235783Skib 714235783Skib retry++; 715235783Skib } while (1); 716235783Skib 717235783Skiberr: 718235783Skib list_for_each_entry_continue_reverse(obj, objects, exec_list) { 719235783Skib struct drm_i915_gem_exec_object2 *entry; 720235783Skib 721235783Skib if (!obj->gtt_space) 722235783Skib continue; 723235783Skib 724235783Skib entry = obj->exec_entry; 725235783Skib if (entry->flags & __EXEC_OBJECT_HAS_FENCE) { 726235783Skib i915_gem_object_unpin_fence(obj); 727235783Skib entry->flags &= ~__EXEC_OBJECT_HAS_FENCE; 728235783Skib } 729235783Skib 730235783Skib i915_gem_object_unpin(obj); 731235783Skib } 732235783Skib 733235783Skib return ret; 734235783Skib} 735235783Skib 736235783Skibstatic int 737235783Skibi915_gem_execbuffer_relocate_slow(struct drm_device *dev, 738235783Skib struct drm_file *file, struct intel_ring_buffer *ring, 739235783Skib struct list_head *objects, struct eb_objects *eb, 740235783Skib struct drm_i915_gem_exec_object2 *exec, int count) 741235783Skib{ 742235783Skib struct drm_i915_gem_relocation_entry *reloc; 743235783Skib struct drm_i915_gem_object *obj; 744235783Skib int *reloc_offset; 745235783Skib int i, total, ret; 746235783Skib 747235783Skib /* We may process another execbuffer during the unlock... */ 748235783Skib while (!list_empty(objects)) { 749235783Skib obj = list_first_entry(objects, 750235783Skib struct drm_i915_gem_object, 751235783Skib exec_list); 752235783Skib list_del_init(&obj->exec_list); 753235783Skib drm_gem_object_unreference(&obj->base); 754235783Skib } 755235783Skib 756235783Skib DRM_UNLOCK(dev); 757235783Skib 758235783Skib total = 0; 759235783Skib for (i = 0; i < count; i++) 760235783Skib total += exec[i].relocation_count; 761235783Skib 762235783Skib reloc_offset = malloc(count * sizeof(*reloc_offset), DRM_I915_GEM, 763235783Skib M_WAITOK | M_ZERO); 764235783Skib reloc = malloc(total * sizeof(*reloc), DRM_I915_GEM, M_WAITOK | M_ZERO); 765235783Skib 766235783Skib total = 0; 767235783Skib for (i = 0; i < count; i++) { 768235783Skib struct drm_i915_gem_relocation_entry *user_relocs; 769235783Skib 770235783Skib user_relocs = (void *)(uintptr_t)exec[i].relocs_ptr; 771235783Skib ret = -copyin(user_relocs, reloc + total, 772235783Skib exec[i].relocation_count * sizeof(*reloc)); 773235783Skib if (ret != 0) { 774235783Skib DRM_LOCK(dev); 775235783Skib goto err; 776235783Skib } 777235783Skib 778235783Skib reloc_offset[i] = total; 779235783Skib total += exec[i].relocation_count; 780235783Skib } 781235783Skib 782235783Skib ret = i915_mutex_lock_interruptible(dev); 783235783Skib if (ret) { 784235783Skib DRM_LOCK(dev); 785235783Skib goto err; 786235783Skib } 787235783Skib 788235783Skib /* reacquire the objects */ 789235783Skib eb_reset(eb); 790235783Skib for (i = 0; i < count; i++) { 791235783Skib struct drm_i915_gem_object *obj; 792235783Skib 793235783Skib obj = to_intel_bo(drm_gem_object_lookup(dev, file, 794235783Skib exec[i].handle)); 795235783Skib if (&obj->base == NULL) { 796235783Skib DRM_DEBUG("Invalid object handle %d at index %d\n", 797235783Skib exec[i].handle, i); 798235783Skib ret = -ENOENT; 799235783Skib goto err; 800235783Skib } 801235783Skib 802235783Skib list_add_tail(&obj->exec_list, objects); 803235783Skib obj->exec_handle = exec[i].handle; 804235783Skib obj->exec_entry = &exec[i]; 805235783Skib eb_add_object(eb, obj); 806235783Skib } 807235783Skib 808235783Skib ret = i915_gem_execbuffer_reserve(ring, file, objects); 809235783Skib if (ret) 810235783Skib goto err; 811235783Skib 812235783Skib list_for_each_entry(obj, objects, exec_list) { 813235783Skib int offset = obj->exec_entry - exec; 814235783Skib ret = i915_gem_execbuffer_relocate_object_slow(obj, eb, 815235783Skib reloc + reloc_offset[offset]); 816235783Skib if (ret) 817235783Skib goto err; 818235783Skib } 819235783Skib 820235783Skib /* Leave the user relocations as are, this is the painfully slow path, 821235783Skib * and we want to avoid the complication of dropping the lock whilst 822235783Skib * having buffers reserved in the aperture and so causing spurious 823235783Skib * ENOSPC for random operations. 824235783Skib */ 825235783Skib 826235783Skiberr: 827235783Skib free(reloc, DRM_I915_GEM); 828235783Skib free(reloc_offset, DRM_I915_GEM); 829235783Skib return ret; 830235783Skib} 831235783Skib 832235783Skibstatic int 833235783Skibi915_gem_execbuffer_flush(struct drm_device *dev, 834235783Skib uint32_t invalidate_domains, 835235783Skib uint32_t flush_domains, 836235783Skib uint32_t flush_rings) 837235783Skib{ 838235783Skib drm_i915_private_t *dev_priv = dev->dev_private; 839235783Skib int i, ret; 840235783Skib 841235783Skib if (flush_domains & I915_GEM_DOMAIN_CPU) 842235783Skib intel_gtt_chipset_flush(); 843235783Skib 844235783Skib if (flush_domains & I915_GEM_DOMAIN_GTT) 845235783Skib wmb(); 846235783Skib 847235783Skib if ((flush_domains | invalidate_domains) & I915_GEM_GPU_DOMAINS) { 848235783Skib for (i = 0; i < I915_NUM_RINGS; i++) 849235783Skib if (flush_rings & (1 << i)) { 850235783Skib ret = i915_gem_flush_ring(&dev_priv->rings[i], 851235783Skib invalidate_domains, flush_domains); 852235783Skib if (ret) 853235783Skib return ret; 854235783Skib } 855235783Skib } 856235783Skib 857235783Skib return 0; 858235783Skib} 859235783Skib 860235783Skibstatic int 861235783Skibi915_gem_execbuffer_wait_for_flips(struct intel_ring_buffer *ring, u32 flips) 862235783Skib{ 863235783Skib u32 plane, flip_mask; 864235783Skib int ret; 865235783Skib 866235783Skib /* Check for any pending flips. As we only maintain a flip queue depth 867235783Skib * of 1, we can simply insert a WAIT for the next display flip prior 868235783Skib * to executing the batch and avoid stalling the CPU. 869235783Skib */ 870235783Skib 871235783Skib for (plane = 0; flips >> plane; plane++) { 872235783Skib if (((flips >> plane) & 1) == 0) 873235783Skib continue; 874235783Skib 875235783Skib if (plane) 876235783Skib flip_mask = MI_WAIT_FOR_PLANE_B_FLIP; 877235783Skib else 878235783Skib flip_mask = MI_WAIT_FOR_PLANE_A_FLIP; 879235783Skib 880235783Skib ret = intel_ring_begin(ring, 2); 881235783Skib if (ret) 882235783Skib return ret; 883235783Skib 884235783Skib intel_ring_emit(ring, MI_WAIT_FOR_EVENT | flip_mask); 885235783Skib intel_ring_emit(ring, MI_NOOP); 886235783Skib intel_ring_advance(ring); 887235783Skib } 888235783Skib 889235783Skib return 0; 890235783Skib} 891235783Skib 892235783Skibstatic int 893235783Skibi915_gem_execbuffer_move_to_gpu(struct intel_ring_buffer *ring, 894235783Skib struct list_head *objects) 895235783Skib{ 896235783Skib struct drm_i915_gem_object *obj; 897235783Skib struct change_domains cd; 898235783Skib int ret; 899235783Skib 900235783Skib memset(&cd, 0, sizeof(cd)); 901235783Skib list_for_each_entry(obj, objects, exec_list) 902235783Skib i915_gem_object_set_to_gpu_domain(obj, ring, &cd); 903235783Skib 904235783Skib if (cd.invalidate_domains | cd.flush_domains) { 905235783Skib#if WATCH_EXEC 906235783Skib DRM_INFO("%s: invalidate_domains %08x flush_domains %08x\n", 907235783Skib __func__, 908235783Skib cd.invalidate_domains, 909235783Skib cd.flush_domains); 910235783Skib#endif 911235783Skib ret = i915_gem_execbuffer_flush(ring->dev, 912235783Skib cd.invalidate_domains, 913235783Skib cd.flush_domains, 914235783Skib cd.flush_rings); 915235783Skib if (ret) 916235783Skib return ret; 917235783Skib } 918235783Skib 919235783Skib if (cd.flips) { 920235783Skib ret = i915_gem_execbuffer_wait_for_flips(ring, cd.flips); 921235783Skib if (ret) 922235783Skib return ret; 923235783Skib } 924235783Skib 925235783Skib list_for_each_entry(obj, objects, exec_list) { 926280369Skib ret = i915_gem_object_sync(obj, ring); 927235783Skib if (ret) 928235783Skib return ret; 929235783Skib } 930235783Skib 931235783Skib return 0; 932235783Skib} 933235783Skib 934235783Skibstatic bool 935235783Skibi915_gem_check_execbuffer(struct drm_i915_gem_execbuffer2 *exec) 936235783Skib{ 937235783Skib return ((exec->batch_start_offset | exec->batch_len) & 0x7) == 0; 938235783Skib} 939235783Skib 940235783Skibstatic int 941235783Skibvalidate_exec_list(struct drm_i915_gem_exec_object2 *exec, int count, 942290454Sjhb vm_page_t ***map, int **maplen) 943235783Skib{ 944235783Skib vm_page_t *ma; 945235783Skib int i, length, page_count; 946235783Skib 947235783Skib /* XXXKIB various limits checking is missing there */ 948235783Skib *map = malloc(count * sizeof(*ma), DRM_I915_GEM, M_WAITOK | M_ZERO); 949290454Sjhb *maplen = malloc(count * sizeof(*maplen), DRM_I915_GEM, M_WAITOK | 950290454Sjhb M_ZERO); 951235783Skib for (i = 0; i < count; i++) { 952235783Skib /* First check for malicious input causing overflow */ 953235783Skib if (exec[i].relocation_count > 954235783Skib INT_MAX / sizeof(struct drm_i915_gem_relocation_entry)) 955235783Skib return -EINVAL; 956235783Skib 957235783Skib length = exec[i].relocation_count * 958235783Skib sizeof(struct drm_i915_gem_relocation_entry); 959235783Skib if (length == 0) { 960235783Skib (*map)[i] = NULL; 961235783Skib continue; 962235783Skib } 963235783Skib /* 964235783Skib * Since both start and end of the relocation region 965235783Skib * may be not aligned on the page boundary, be 966235783Skib * conservative and request a page slot for each 967235783Skib * partial page. Thus +2. 968235783Skib */ 969235783Skib page_count = howmany(length, PAGE_SIZE) + 2; 970235783Skib ma = (*map)[i] = malloc(page_count * sizeof(vm_page_t), 971235783Skib DRM_I915_GEM, M_WAITOK | M_ZERO); 972290454Sjhb (*maplen)[i] = vm_fault_quick_hold_pages( 973290454Sjhb &curproc->p_vmspace->vm_map, exec[i].relocs_ptr, length, 974290454Sjhb VM_PROT_READ | VM_PROT_WRITE, ma, page_count); 975290454Sjhb if ((*maplen)[i] == -1) { 976235783Skib free(ma, DRM_I915_GEM); 977235783Skib (*map)[i] = NULL; 978235783Skib return (-EFAULT); 979235783Skib } 980235783Skib } 981235783Skib 982235783Skib return 0; 983235783Skib} 984235783Skib 985235783Skibstatic void 986235783Skibi915_gem_execbuffer_move_to_active(struct list_head *objects, 987235783Skib struct intel_ring_buffer *ring, 988235783Skib u32 seqno) 989235783Skib{ 990235783Skib struct drm_i915_gem_object *obj; 991235783Skib uint32_t old_read, old_write; 992235783Skib 993235783Skib list_for_each_entry(obj, objects, exec_list) { 994235783Skib old_read = obj->base.read_domains; 995235783Skib old_write = obj->base.write_domain; 996235783Skib 997235783Skib obj->base.read_domains = obj->base.pending_read_domains; 998235783Skib obj->base.write_domain = obj->base.pending_write_domain; 999235783Skib obj->fenced_gpu_access = obj->pending_fenced_gpu_access; 1000235783Skib 1001235783Skib i915_gem_object_move_to_active(obj, ring, seqno); 1002235783Skib if (obj->base.write_domain) { 1003235783Skib obj->dirty = 1; 1004235783Skib obj->pending_gpu_write = true; 1005235783Skib list_move_tail(&obj->gpu_write_list, 1006235783Skib &ring->gpu_write_list); 1007280369Skib if (obj->pin_count) /* check for potential scanout */ 1008280369Skib intel_mark_busy(ring->dev, obj); 1009235783Skib } 1010235783Skib CTR3(KTR_DRM, "object_change_domain move_to_active %p %x %x", 1011235783Skib obj, old_read, old_write); 1012235783Skib } 1013280369Skib 1014280369Skib intel_mark_busy(ring->dev, NULL); 1015235783Skib} 1016235783Skib 1017235783Skibint i915_gem_sync_exec_requests; 1018235783Skib 1019235783Skibstatic void 1020235783Skibi915_gem_execbuffer_retire_commands(struct drm_device *dev, 1021235783Skib struct drm_file *file, 1022235783Skib struct intel_ring_buffer *ring) 1023235783Skib{ 1024235783Skib struct drm_i915_gem_request *request; 1025235783Skib u32 invalidate; 1026235783Skib 1027235783Skib /* 1028235783Skib * Ensure that the commands in the batch buffer are 1029235783Skib * finished before the interrupt fires. 1030235783Skib * 1031235783Skib * The sampler always gets flushed on i965 (sigh). 1032235783Skib */ 1033235783Skib invalidate = I915_GEM_DOMAIN_COMMAND; 1034235783Skib if (INTEL_INFO(dev)->gen >= 4) 1035235783Skib invalidate |= I915_GEM_DOMAIN_SAMPLER; 1036235783Skib if (ring->flush(ring, invalidate, 0)) { 1037235783Skib i915_gem_next_request_seqno(ring); 1038235783Skib return; 1039235783Skib } 1040235783Skib 1041235783Skib /* Add a breadcrumb for the completion of the batch buffer */ 1042235783Skib request = malloc(sizeof(*request), DRM_I915_GEM, M_WAITOK | M_ZERO); 1043235783Skib if (request == NULL || i915_add_request(ring, file, request)) { 1044235783Skib i915_gem_next_request_seqno(ring); 1045235783Skib free(request, DRM_I915_GEM); 1046280369Skib } else if (i915_gem_sync_exec_requests) { 1047280369Skib i915_wait_request(ring, request->seqno); 1048280369Skib i915_gem_retire_requests(dev); 1049280369Skib } 1050235783Skib} 1051235783Skib 1052235783Skibstatic void 1053235783Skibi915_gem_fix_mi_batchbuffer_end(struct drm_i915_gem_object *batch_obj, 1054235783Skib uint32_t batch_start_offset, uint32_t batch_len) 1055235783Skib{ 1056235783Skib char *mkva; 1057235783Skib uint64_t po_r, po_w; 1058235783Skib uint32_t cmd; 1059235783Skib 1060235783Skib po_r = batch_obj->base.dev->agp->base + batch_obj->gtt_offset + 1061235783Skib batch_start_offset + batch_len; 1062235783Skib if (batch_len > 0) 1063235783Skib po_r -= 4; 1064235783Skib mkva = pmap_mapdev_attr(trunc_page(po_r), 2 * PAGE_SIZE, 1065235783Skib PAT_WRITE_COMBINING); 1066236182Skib po_r &= PAGE_MASK; 1067236182Skib cmd = *(uint32_t *)(mkva + po_r); 1068235783Skib 1069235783Skib if (cmd != MI_BATCH_BUFFER_END) { 1070235783Skib /* 1071235783Skib * batch_len != 0 due to the check at the start of 1072235783Skib * i915_gem_do_execbuffer 1073235783Skib */ 1074235783Skib if (batch_obj->base.size > batch_start_offset + batch_len) { 1075235783Skib po_w = po_r + 4; 1076235783Skib/* DRM_DEBUG("batchbuffer does not end by MI_BATCH_BUFFER_END !\n"); */ 1077235783Skib } else { 1078235783Skib po_w = po_r; 1079235783SkibDRM_DEBUG("batchbuffer does not end by MI_BATCH_BUFFER_END, overwriting last bo cmd !\n"); 1080235783Skib } 1081236182Skib *(uint32_t *)(mkva + po_w) = MI_BATCH_BUFFER_END; 1082235783Skib } 1083235783Skib 1084235783Skib pmap_unmapdev((vm_offset_t)mkva, 2 * PAGE_SIZE); 1085235783Skib} 1086235783Skib 1087236183Skibint i915_fix_mi_batchbuffer_end = 0; 1088235783Skib 1089235783Skib static int 1090235783Skibi915_reset_gen7_sol_offsets(struct drm_device *dev, 1091235783Skib struct intel_ring_buffer *ring) 1092235783Skib{ 1093235783Skib drm_i915_private_t *dev_priv = dev->dev_private; 1094235783Skib int ret, i; 1095235783Skib 1096235783Skib if (!IS_GEN7(dev) || ring != &dev_priv->rings[RCS]) 1097235783Skib return 0; 1098235783Skib 1099235783Skib ret = intel_ring_begin(ring, 4 * 3); 1100235783Skib if (ret) 1101235783Skib return ret; 1102235783Skib 1103235783Skib for (i = 0; i < 4; i++) { 1104235783Skib intel_ring_emit(ring, MI_LOAD_REGISTER_IMM(1)); 1105235783Skib intel_ring_emit(ring, GEN7_SO_WRITE_OFFSET(i)); 1106235783Skib intel_ring_emit(ring, 0); 1107235783Skib } 1108235783Skib 1109235783Skib intel_ring_advance(ring); 1110235783Skib 1111235783Skib return 0; 1112235783Skib} 1113235783Skib 1114235783Skibstatic int 1115235783Skibi915_gem_do_execbuffer(struct drm_device *dev, void *data, 1116235783Skib struct drm_file *file, 1117235783Skib struct drm_i915_gem_execbuffer2 *args, 1118235783Skib struct drm_i915_gem_exec_object2 *exec) 1119235783Skib{ 1120235783Skib drm_i915_private_t *dev_priv = dev->dev_private; 1121235783Skib struct list_head objects; 1122235783Skib struct eb_objects *eb; 1123235783Skib struct drm_i915_gem_object *batch_obj; 1124235783Skib struct drm_clip_rect *cliprects = NULL; 1125235783Skib struct intel_ring_buffer *ring; 1126235783Skib vm_page_t **relocs_ma; 1127290454Sjhb int *relocs_len; 1128271816Sdumbbell u32 ctx_id = i915_execbuffer2_get_context_id(*args); 1129235783Skib u32 exec_start, exec_len; 1130235783Skib u32 seqno; 1131235783Skib u32 mask; 1132235783Skib int ret, mode, i; 1133235783Skib 1134235783Skib if (!i915_gem_check_execbuffer(args)) { 1135235783Skib DRM_DEBUG("execbuf with invalid offset/length\n"); 1136235783Skib return -EINVAL; 1137235783Skib } 1138235783Skib 1139235783Skib if (args->batch_len == 0) 1140235783Skib return (0); 1141235783Skib 1142290454Sjhb ret = validate_exec_list(exec, args->buffer_count, &relocs_ma, 1143290454Sjhb &relocs_len); 1144235783Skib if (ret != 0) 1145235783Skib goto pre_struct_lock_err; 1146235783Skib 1147235783Skib switch (args->flags & I915_EXEC_RING_MASK) { 1148235783Skib case I915_EXEC_DEFAULT: 1149235783Skib case I915_EXEC_RENDER: 1150235783Skib ring = &dev_priv->rings[RCS]; 1151235783Skib break; 1152235783Skib case I915_EXEC_BSD: 1153235783Skib ring = &dev_priv->rings[VCS]; 1154271816Sdumbbell if (ctx_id != 0) { 1155271816Sdumbbell DRM_DEBUG("Ring %s doesn't support contexts\n", 1156271816Sdumbbell ring->name); 1157290454Sjhb ret = -EPERM; 1158290454Sjhb goto pre_struct_lock_err; 1159271816Sdumbbell } 1160235783Skib break; 1161235783Skib case I915_EXEC_BLT: 1162235783Skib ring = &dev_priv->rings[BCS]; 1163271816Sdumbbell if (ctx_id != 0) { 1164271816Sdumbbell DRM_DEBUG("Ring %s doesn't support contexts\n", 1165271816Sdumbbell ring->name); 1166290454Sjhb ret = -EPERM; 1167290454Sjhb goto pre_struct_lock_err; 1168271816Sdumbbell } 1169235783Skib break; 1170235783Skib default: 1171235783Skib DRM_DEBUG("execbuf with unknown ring: %d\n", 1172235783Skib (int)(args->flags & I915_EXEC_RING_MASK)); 1173235783Skib ret = -EINVAL; 1174235783Skib goto pre_struct_lock_err; 1175235783Skib } 1176280369Skib if (!intel_ring_initialized(ring)) { 1177280369Skib DRM_DEBUG("execbuf with invalid ring: %d\n", 1178280369Skib (int)(args->flags & I915_EXEC_RING_MASK)); 1179290454Sjhb ret = -EINVAL; 1180290454Sjhb goto pre_struct_lock_err; 1181280369Skib } 1182235783Skib 1183235783Skib mode = args->flags & I915_EXEC_CONSTANTS_MASK; 1184235783Skib mask = I915_EXEC_CONSTANTS_MASK; 1185235783Skib switch (mode) { 1186235783Skib case I915_EXEC_CONSTANTS_REL_GENERAL: 1187235783Skib case I915_EXEC_CONSTANTS_ABSOLUTE: 1188235783Skib case I915_EXEC_CONSTANTS_REL_SURFACE: 1189235783Skib if (ring == &dev_priv->rings[RCS] && 1190235783Skib mode != dev_priv->relative_constants_mode) { 1191235783Skib if (INTEL_INFO(dev)->gen < 4) { 1192235783Skib ret = -EINVAL; 1193235783Skib goto pre_struct_lock_err; 1194235783Skib } 1195235783Skib 1196235783Skib if (INTEL_INFO(dev)->gen > 5 && 1197235783Skib mode == I915_EXEC_CONSTANTS_REL_SURFACE) { 1198235783Skib ret = -EINVAL; 1199235783Skib goto pre_struct_lock_err; 1200235783Skib } 1201235783Skib 1202235783Skib /* The HW changed the meaning on this bit on gen6 */ 1203235783Skib if (INTEL_INFO(dev)->gen >= 6) 1204235783Skib mask &= ~I915_EXEC_CONSTANTS_REL_SURFACE; 1205235783Skib } 1206235783Skib break; 1207235783Skib default: 1208235783Skib DRM_DEBUG("execbuf with unknown constants: %d\n", mode); 1209235783Skib ret = -EINVAL; 1210235783Skib goto pre_struct_lock_err; 1211235783Skib } 1212235783Skib 1213235783Skib if (args->buffer_count < 1) { 1214235783Skib DRM_DEBUG("execbuf with %d buffers\n", args->buffer_count); 1215235783Skib ret = -EINVAL; 1216235783Skib goto pre_struct_lock_err; 1217235783Skib } 1218235783Skib 1219235783Skib if (args->num_cliprects != 0) { 1220235783Skib if (ring != &dev_priv->rings[RCS]) { 1221235783Skib DRM_DEBUG("clip rectangles are only valid with the render ring\n"); 1222235783Skib ret = -EINVAL; 1223235783Skib goto pre_struct_lock_err; 1224235783Skib } 1225235783Skib 1226280369Skib if (INTEL_INFO(dev)->gen >= 5) { 1227280369Skib DRM_DEBUG("clip rectangles are only valid on pre-gen5\n"); 1228280369Skib ret = -EINVAL; 1229280369Skib goto pre_struct_lock_err; 1230280369Skib } 1231280369Skib 1232235783Skib if (args->num_cliprects > UINT_MAX / sizeof(*cliprects)) { 1233235783Skib DRM_DEBUG("execbuf with %u cliprects\n", 1234235783Skib args->num_cliprects); 1235235783Skib ret = -EINVAL; 1236235783Skib goto pre_struct_lock_err; 1237235783Skib } 1238235783Skib cliprects = malloc( sizeof(*cliprects) * args->num_cliprects, 1239235783Skib DRM_I915_GEM, M_WAITOK | M_ZERO); 1240235783Skib ret = -copyin((void *)(uintptr_t)args->cliprects_ptr, cliprects, 1241235783Skib sizeof(*cliprects) * args->num_cliprects); 1242235783Skib if (ret != 0) 1243235783Skib goto pre_struct_lock_err; 1244235783Skib } 1245235783Skib 1246235783Skib ret = i915_mutex_lock_interruptible(dev); 1247235783Skib if (ret) 1248235783Skib goto pre_struct_lock_err; 1249235783Skib 1250235783Skib if (dev_priv->mm.suspended) { 1251282199Sdumbbell DRM_UNLOCK(dev); 1252235783Skib ret = -EBUSY; 1253282199Sdumbbell goto pre_struct_lock_err; 1254235783Skib } 1255235783Skib 1256235783Skib eb = eb_create(args->buffer_count); 1257235783Skib if (eb == NULL) { 1258282199Sdumbbell DRM_UNLOCK(dev); 1259235783Skib ret = -ENOMEM; 1260282199Sdumbbell goto pre_struct_lock_err; 1261235783Skib } 1262235783Skib 1263235783Skib /* Look up object handles */ 1264235783Skib INIT_LIST_HEAD(&objects); 1265235783Skib for (i = 0; i < args->buffer_count; i++) { 1266235783Skib struct drm_i915_gem_object *obj; 1267235783Skib obj = to_intel_bo(drm_gem_object_lookup(dev, file, 1268235783Skib exec[i].handle)); 1269235783Skib if (&obj->base == NULL) { 1270235783Skib DRM_DEBUG("Invalid object handle %d at index %d\n", 1271235783Skib exec[i].handle, i); 1272235783Skib /* prevent error path from reading uninitialized data */ 1273235783Skib ret = -ENOENT; 1274235783Skib goto err; 1275235783Skib } 1276235783Skib 1277235783Skib if (!list_empty(&obj->exec_list)) { 1278235783Skib DRM_DEBUG("Object %p [handle %d, index %d] appears more than once in object list\n", 1279235783Skib obj, exec[i].handle, i); 1280235783Skib ret = -EINVAL; 1281235783Skib goto err; 1282235783Skib } 1283235783Skib 1284235783Skib list_add_tail(&obj->exec_list, &objects); 1285235783Skib obj->exec_handle = exec[i].handle; 1286235783Skib obj->exec_entry = &exec[i]; 1287235783Skib eb_add_object(eb, obj); 1288235783Skib } 1289235783Skib 1290235783Skib /* take note of the batch buffer before we might reorder the lists */ 1291235783Skib batch_obj = list_entry(objects.prev, 1292235783Skib struct drm_i915_gem_object, 1293235783Skib exec_list); 1294235783Skib 1295235783Skib /* Move the objects en-masse into the GTT, evicting if necessary. */ 1296235783Skib ret = i915_gem_execbuffer_reserve(ring, file, &objects); 1297235783Skib if (ret) 1298235783Skib goto err; 1299235783Skib 1300235783Skib /* The objects are in their final locations, apply the relocations. */ 1301235783Skib ret = i915_gem_execbuffer_relocate(dev, eb, &objects); 1302235783Skib if (ret) { 1303235783Skib if (ret == -EFAULT) { 1304235783Skib ret = i915_gem_execbuffer_relocate_slow(dev, file, ring, 1305235783Skib &objects, eb, exec, args->buffer_count); 1306235783Skib DRM_LOCK_ASSERT(dev); 1307235783Skib } 1308235783Skib if (ret) 1309235783Skib goto err; 1310235783Skib } 1311235783Skib 1312235783Skib /* Set the pending read domains for the batch buffer to COMMAND */ 1313235783Skib if (batch_obj->base.pending_write_domain) { 1314235783Skib DRM_DEBUG("Attempting to use self-modifying batch buffer\n"); 1315235783Skib ret = -EINVAL; 1316235783Skib goto err; 1317235783Skib } 1318235783Skib batch_obj->base.pending_read_domains |= I915_GEM_DOMAIN_COMMAND; 1319235783Skib 1320235783Skib ret = i915_gem_execbuffer_move_to_gpu(ring, &objects); 1321235783Skib if (ret) 1322235783Skib goto err; 1323235783Skib 1324271816Sdumbbell ret = i915_switch_context(ring, file, ctx_id); 1325271816Sdumbbell if (ret) 1326271816Sdumbbell goto err; 1327271816Sdumbbell 1328235783Skib seqno = i915_gem_next_request_seqno(ring); 1329235783Skib for (i = 0; i < I915_NUM_RINGS - 1; i++) { 1330235783Skib if (seqno < ring->sync_seqno[i]) { 1331235783Skib /* The GPU can not handle its semaphore value wrapping, 1332235783Skib * so every billion or so execbuffers, we need to stall 1333235783Skib * the GPU in order to reset the counters. 1334235783Skib */ 1335280369Skib ret = i915_gpu_idle(dev); 1336235783Skib if (ret) 1337235783Skib goto err; 1338280369Skib i915_gem_retire_requests(dev); 1339235783Skib 1340235783Skib KASSERT(ring->sync_seqno[i] == 0, ("Non-zero sync_seqno")); 1341235783Skib } 1342235783Skib } 1343235783Skib 1344235783Skib if (ring == &dev_priv->rings[RCS] && 1345235783Skib mode != dev_priv->relative_constants_mode) { 1346235783Skib ret = intel_ring_begin(ring, 4); 1347235783Skib if (ret) 1348235783Skib goto err; 1349235783Skib 1350235783Skib intel_ring_emit(ring, MI_NOOP); 1351235783Skib intel_ring_emit(ring, MI_LOAD_REGISTER_IMM(1)); 1352235783Skib intel_ring_emit(ring, INSTPM); 1353235783Skib intel_ring_emit(ring, mask << 16 | mode); 1354235783Skib intel_ring_advance(ring); 1355235783Skib 1356235783Skib dev_priv->relative_constants_mode = mode; 1357235783Skib } 1358235783Skib 1359235783Skib if (args->flags & I915_EXEC_GEN7_SOL_RESET) { 1360235783Skib ret = i915_reset_gen7_sol_offsets(dev, ring); 1361235783Skib if (ret) 1362235783Skib goto err; 1363235783Skib } 1364235783Skib 1365235783Skib exec_start = batch_obj->gtt_offset + args->batch_start_offset; 1366235783Skib exec_len = args->batch_len; 1367235783Skib 1368235783Skib if (i915_fix_mi_batchbuffer_end) { 1369235783Skib i915_gem_fix_mi_batchbuffer_end(batch_obj, 1370235783Skib args->batch_start_offset, args->batch_len); 1371235783Skib } 1372235783Skib 1373235783Skib CTR4(KTR_DRM, "ring_dispatch %s %d exec %x %x", ring->name, seqno, 1374235783Skib exec_start, exec_len); 1375235783Skib 1376235783Skib if (cliprects) { 1377235783Skib for (i = 0; i < args->num_cliprects; i++) { 1378235783Skib ret = i915_emit_box_p(dev, &cliprects[i], 1379235783Skib args->DR1, args->DR4); 1380235783Skib if (ret) 1381235783Skib goto err; 1382235783Skib 1383235783Skib ret = ring->dispatch_execbuffer(ring, exec_start, 1384235783Skib exec_len); 1385235783Skib if (ret) 1386235783Skib goto err; 1387235783Skib } 1388235783Skib } else { 1389235783Skib ret = ring->dispatch_execbuffer(ring, exec_start, exec_len); 1390235783Skib if (ret) 1391235783Skib goto err; 1392235783Skib } 1393235783Skib 1394235783Skib i915_gem_execbuffer_move_to_active(&objects, ring, seqno); 1395235783Skib i915_gem_execbuffer_retire_commands(dev, file, ring); 1396235783Skib 1397235783Skiberr: 1398235783Skib eb_destroy(eb); 1399235783Skib while (!list_empty(&objects)) { 1400235783Skib struct drm_i915_gem_object *obj; 1401235783Skib 1402235783Skib obj = list_first_entry(&objects, struct drm_i915_gem_object, 1403235783Skib exec_list); 1404235783Skib list_del_init(&obj->exec_list); 1405235783Skib drm_gem_object_unreference(&obj->base); 1406235783Skib } 1407235783Skib DRM_UNLOCK(dev); 1408235783Skib 1409235783Skibpre_struct_lock_err: 1410235783Skib for (i = 0; i < args->buffer_count; i++) { 1411235783Skib if (relocs_ma[i] != NULL) { 1412290454Sjhb vm_page_unhold_pages(relocs_ma[i], relocs_len[i]); 1413235783Skib free(relocs_ma[i], DRM_I915_GEM); 1414235783Skib } 1415235783Skib } 1416290454Sjhb free(relocs_len, DRM_I915_GEM); 1417235783Skib free(relocs_ma, DRM_I915_GEM); 1418235783Skib free(cliprects, DRM_I915_GEM); 1419235783Skib return ret; 1420235783Skib} 1421235783Skib 1422235783Skib/* 1423235783Skib * Legacy execbuffer just creates an exec2 list from the original exec object 1424235783Skib * list array and passes it to the real function. 1425235783Skib */ 1426235783Skibint 1427235783Skibi915_gem_execbuffer(struct drm_device *dev, void *data, 1428235783Skib struct drm_file *file) 1429235783Skib{ 1430235783Skib struct drm_i915_gem_execbuffer *args = data; 1431235783Skib struct drm_i915_gem_execbuffer2 exec2; 1432235783Skib struct drm_i915_gem_exec_object *exec_list = NULL; 1433235783Skib struct drm_i915_gem_exec_object2 *exec2_list = NULL; 1434235783Skib int ret, i; 1435235783Skib 1436235783Skib DRM_DEBUG("buffers_ptr %d buffer_count %d len %08x\n", 1437235783Skib (int) args->buffers_ptr, args->buffer_count, args->batch_len); 1438235783Skib 1439235783Skib if (args->buffer_count < 1) { 1440235783Skib DRM_DEBUG("execbuf with %d buffers\n", args->buffer_count); 1441235783Skib return -EINVAL; 1442235783Skib } 1443235783Skib 1444235783Skib /* Copy in the exec list from userland */ 1445235783Skib /* XXXKIB user-controlled malloc size */ 1446235783Skib exec_list = malloc(sizeof(*exec_list) * args->buffer_count, 1447235783Skib DRM_I915_GEM, M_WAITOK); 1448235783Skib exec2_list = malloc(sizeof(*exec2_list) * args->buffer_count, 1449235783Skib DRM_I915_GEM, M_WAITOK); 1450235783Skib ret = -copyin((void *)(uintptr_t)args->buffers_ptr, exec_list, 1451235783Skib sizeof(*exec_list) * args->buffer_count); 1452235783Skib if (ret != 0) { 1453235783Skib DRM_DEBUG("copy %d exec entries failed %d\n", 1454235783Skib args->buffer_count, ret); 1455235783Skib free(exec_list, DRM_I915_GEM); 1456235783Skib free(exec2_list, DRM_I915_GEM); 1457235783Skib return (ret); 1458235783Skib } 1459235783Skib 1460235783Skib for (i = 0; i < args->buffer_count; i++) { 1461235783Skib exec2_list[i].handle = exec_list[i].handle; 1462235783Skib exec2_list[i].relocation_count = exec_list[i].relocation_count; 1463235783Skib exec2_list[i].relocs_ptr = exec_list[i].relocs_ptr; 1464235783Skib exec2_list[i].alignment = exec_list[i].alignment; 1465235783Skib exec2_list[i].offset = exec_list[i].offset; 1466235783Skib if (INTEL_INFO(dev)->gen < 4) 1467235783Skib exec2_list[i].flags = EXEC_OBJECT_NEEDS_FENCE; 1468235783Skib else 1469235783Skib exec2_list[i].flags = 0; 1470235783Skib } 1471235783Skib 1472235783Skib exec2.buffers_ptr = args->buffers_ptr; 1473235783Skib exec2.buffer_count = args->buffer_count; 1474235783Skib exec2.batch_start_offset = args->batch_start_offset; 1475235783Skib exec2.batch_len = args->batch_len; 1476235783Skib exec2.DR1 = args->DR1; 1477235783Skib exec2.DR4 = args->DR4; 1478235783Skib exec2.num_cliprects = args->num_cliprects; 1479235783Skib exec2.cliprects_ptr = args->cliprects_ptr; 1480235783Skib exec2.flags = I915_EXEC_RENDER; 1481271816Sdumbbell i915_execbuffer2_set_context_id(exec2, 0); 1482235783Skib 1483235783Skib ret = i915_gem_do_execbuffer(dev, data, file, &exec2, exec2_list); 1484235783Skib if (!ret) { 1485235783Skib /* Copy the new buffer offsets back to the user's exec list. */ 1486235783Skib for (i = 0; i < args->buffer_count; i++) 1487235783Skib exec_list[i].offset = exec2_list[i].offset; 1488235783Skib /* ... and back out to userspace */ 1489235783Skib ret = -copyout(exec_list, (void *)(uintptr_t)args->buffers_ptr, 1490235783Skib sizeof(*exec_list) * args->buffer_count); 1491235783Skib if (ret != 0) { 1492235783Skib DRM_DEBUG("failed to copy %d exec entries " 1493235783Skib "back to user (%d)\n", 1494235783Skib args->buffer_count, ret); 1495235783Skib } 1496235783Skib } 1497235783Skib 1498235783Skib free(exec_list, DRM_I915_GEM); 1499235783Skib free(exec2_list, DRM_I915_GEM); 1500235783Skib return ret; 1501235783Skib} 1502235783Skib 1503235783Skibint 1504235783Skibi915_gem_execbuffer2(struct drm_device *dev, void *data, 1505235783Skib struct drm_file *file) 1506235783Skib{ 1507235783Skib struct drm_i915_gem_execbuffer2 *args = data; 1508235783Skib struct drm_i915_gem_exec_object2 *exec2_list = NULL; 1509235783Skib int ret; 1510235783Skib 1511235783Skib DRM_DEBUG("buffers_ptr %jx buffer_count %d len %08x\n", 1512235783Skib (uintmax_t)args->buffers_ptr, args->buffer_count, args->batch_len); 1513235783Skib 1514235783Skib if (args->buffer_count < 1 || 1515235783Skib args->buffer_count > UINT_MAX / sizeof(*exec2_list)) { 1516235783Skib DRM_DEBUG("execbuf2 with %d buffers\n", args->buffer_count); 1517235783Skib return -EINVAL; 1518235783Skib } 1519235783Skib 1520235783Skib /* XXXKIB user-controllable malloc size */ 1521235783Skib exec2_list = malloc(sizeof(*exec2_list) * args->buffer_count, 1522235783Skib DRM_I915_GEM, M_WAITOK); 1523235783Skib ret = -copyin((void *)(uintptr_t)args->buffers_ptr, exec2_list, 1524235783Skib sizeof(*exec2_list) * args->buffer_count); 1525235783Skib if (ret != 0) { 1526235783Skib DRM_DEBUG("copy %d exec entries failed %d\n", 1527235783Skib args->buffer_count, ret); 1528235783Skib free(exec2_list, DRM_I915_GEM); 1529235783Skib return (ret); 1530235783Skib } 1531235783Skib 1532235783Skib ret = i915_gem_do_execbuffer(dev, data, file, args, exec2_list); 1533235783Skib if (!ret) { 1534235783Skib /* Copy the new buffer offsets back to the user's exec list. */ 1535235783Skib ret = -copyout(exec2_list, (void *)(uintptr_t)args->buffers_ptr, 1536235783Skib sizeof(*exec2_list) * args->buffer_count); 1537235783Skib if (ret) { 1538235783Skib DRM_DEBUG("failed to copy %d exec entries " 1539235783Skib "back to user (%d)\n", 1540235783Skib args->buffer_count, ret); 1541235783Skib } 1542235783Skib } 1543235783Skib 1544235783Skib free(exec2_list, DRM_I915_GEM); 1545235783Skib return ret; 1546235783Skib} 1547