diff options
| author | Eric Anholt <eric@anholt.net> | 2008-11-10 13:53:25 -0500 |
|---|---|---|
| committer | Dave Airlie <airlied@redhat.com> | 2008-12-03 20:21:52 -0500 |
| commit | 2ef7eeaa553d88e78d9a4520271f26a7bc0e2968 (patch) | |
| tree | 7a46a23d4fcc57ae03131c106d21fdac721dfce1 | |
| parent | b670d8158283c35842ae1c650f75c375d8710607 (diff) | |
drm/i915: Make a single set-to-gtt-domain path.
This fixes failure to flush caches in the relocation update path, and
failure to wait in the set_domain ioctl, each of which could lead to incorrect
rendering.
Signed-off-by: Eric Anholt <eric@anholt.net>
Signed-off-by: Dave Airlie <airlied@redhat.com>
| -rw-r--r-- | drivers/gpu/drm/i915/i915_gem.c | 115 |
1 files changed, 96 insertions, 19 deletions
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 3fe108b7e2f..62a059dce85 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c | |||
| @@ -48,6 +48,8 @@ i915_gem_set_domain(struct drm_gem_object *obj, | |||
| 48 | struct drm_file *file_priv, | 48 | struct drm_file *file_priv, |
| 49 | uint32_t read_domains, | 49 | uint32_t read_domains, |
| 50 | uint32_t write_domain); | 50 | uint32_t write_domain); |
| 51 | static int i915_gem_object_set_to_gtt_domain(struct drm_gem_object *obj, | ||
| 52 | int write); | ||
| 51 | static int i915_gem_object_get_page_list(struct drm_gem_object *obj); | 53 | static int i915_gem_object_get_page_list(struct drm_gem_object *obj); |
| 52 | static void i915_gem_object_free_page_list(struct drm_gem_object *obj); | 54 | static void i915_gem_object_free_page_list(struct drm_gem_object *obj); |
| 53 | static int i915_gem_object_wait_rendering(struct drm_gem_object *obj); | 55 | static int i915_gem_object_wait_rendering(struct drm_gem_object *obj); |
| @@ -260,8 +262,7 @@ i915_gem_gtt_pwrite(struct drm_device *dev, struct drm_gem_object *obj, | |||
| 260 | mutex_unlock(&dev->struct_mutex); | 262 | mutex_unlock(&dev->struct_mutex); |
| 261 | return ret; | 263 | return ret; |
| 262 | } | 264 | } |
| 263 | ret = i915_gem_set_domain(obj, file_priv, | 265 | ret = i915_gem_object_set_to_gtt_domain(obj, 1); |
| 264 | I915_GEM_DOMAIN_GTT, I915_GEM_DOMAIN_GTT); | ||
| 265 | if (ret) | 266 | if (ret) |
| 266 | goto fail; | 267 | goto fail; |
| 267 | 268 | ||
| @@ -397,7 +398,8 @@ i915_gem_pwrite_ioctl(struct drm_device *dev, void *data, | |||
| 397 | } | 398 | } |
| 398 | 399 | ||
| 399 | /** | 400 | /** |
| 400 | * Called when user space prepares to use an object | 401 | * Called when user space prepares to use an object with the CPU, either |
| 402 | * through the mmap ioctl's mapping or a GTT mapping. | ||
| 401 | */ | 403 | */ |
| 402 | int | 404 | int |
| 403 | i915_gem_set_domain_ioctl(struct drm_device *dev, void *data, | 405 | i915_gem_set_domain_ioctl(struct drm_device *dev, void *data, |
| @@ -405,11 +407,26 @@ i915_gem_set_domain_ioctl(struct drm_device *dev, void *data, | |||
| 405 | { | 407 | { |
| 406 | struct drm_i915_gem_set_domain *args = data; | 408 | struct drm_i915_gem_set_domain *args = data; |
| 407 | struct drm_gem_object *obj; | 409 | struct drm_gem_object *obj; |
| 410 | uint32_t read_domains = args->read_domains; | ||
| 411 | uint32_t write_domain = args->write_domain; | ||
| 408 | int ret; | 412 | int ret; |
| 409 | 413 | ||
| 410 | if (!(dev->driver->driver_features & DRIVER_GEM)) | 414 | if (!(dev->driver->driver_features & DRIVER_GEM)) |
| 411 | return -ENODEV; | 415 | return -ENODEV; |
| 412 | 416 | ||
| 417 | /* Only handle setting domains to types used by the CPU. */ | ||
| 418 | if (write_domain & ~(I915_GEM_DOMAIN_CPU | I915_GEM_DOMAIN_GTT)) | ||
| 419 | return -EINVAL; | ||
| 420 | |||
| 421 | if (read_domains & ~(I915_GEM_DOMAIN_CPU | I915_GEM_DOMAIN_GTT)) | ||
| 422 | return -EINVAL; | ||
| 423 | |||
| 424 | /* Having something in the write domain implies it's in the read | ||
| 425 | * domain, and only that read domain. Enforce that in the request. | ||
| 426 | */ | ||
| 427 | if (write_domain != 0 && read_domains != write_domain) | ||
| 428 | return -EINVAL; | ||
| 429 | |||
| 413 | obj = drm_gem_object_lookup(dev, file_priv, args->handle); | 430 | obj = drm_gem_object_lookup(dev, file_priv, args->handle); |
| 414 | if (obj == NULL) | 431 | if (obj == NULL) |
| 415 | return -EBADF; | 432 | return -EBADF; |
| @@ -417,10 +434,15 @@ i915_gem_set_domain_ioctl(struct drm_device *dev, void *data, | |||
| 417 | mutex_lock(&dev->struct_mutex); | 434 | mutex_lock(&dev->struct_mutex); |
| 418 | #if WATCH_BUF | 435 | #if WATCH_BUF |
| 419 | DRM_INFO("set_domain_ioctl %p(%d), %08x %08x\n", | 436 | DRM_INFO("set_domain_ioctl %p(%d), %08x %08x\n", |
| 420 | obj, obj->size, args->read_domains, args->write_domain); | 437 | obj, obj->size, read_domains, write_domain); |
| 421 | #endif | 438 | #endif |
| 422 | ret = i915_gem_set_domain(obj, file_priv, | 439 | if (read_domains & I915_GEM_DOMAIN_GTT) { |
| 423 | args->read_domains, args->write_domain); | 440 | ret = i915_gem_object_set_to_gtt_domain(obj, write_domain != 0); |
| 441 | } else { | ||
| 442 | ret = i915_gem_set_domain(obj, file_priv, | ||
| 443 | read_domains, write_domain); | ||
| 444 | } | ||
| 445 | |||
| 424 | drm_gem_object_unreference(obj); | 446 | drm_gem_object_unreference(obj); |
| 425 | mutex_unlock(&dev->struct_mutex); | 447 | mutex_unlock(&dev->struct_mutex); |
| 426 | return ret; | 448 | return ret; |
| @@ -1237,6 +1259,69 @@ i915_gem_clflush_object(struct drm_gem_object *obj) | |||
| 1237 | drm_clflush_pages(obj_priv->page_list, obj->size / PAGE_SIZE); | 1259 | drm_clflush_pages(obj_priv->page_list, obj->size / PAGE_SIZE); |
| 1238 | } | 1260 | } |
| 1239 | 1261 | ||
| 1262 | /** | ||
| 1263 | * Moves a single object to the GTT read, and possibly write domain. | ||
| 1264 | * | ||
| 1265 | * This function returns when the move is complete, including waiting on | ||
| 1266 | * flushes to occur. | ||
| 1267 | */ | ||
| 1268 | static int | ||
| 1269 | i915_gem_object_set_to_gtt_domain(struct drm_gem_object *obj, int write) | ||
| 1270 | { | ||
| 1271 | struct drm_device *dev = obj->dev; | ||
| 1272 | struct drm_i915_gem_object *obj_priv = obj->driver_private; | ||
| 1273 | uint32_t flush_domains; | ||
| 1274 | |||
| 1275 | /* Figure out what GPU domains we need to flush or invalidate for | ||
| 1276 | * moving to GTT. | ||
| 1277 | */ | ||
| 1278 | flush_domains = obj->write_domain & I915_GEM_GPU_DOMAINS; | ||
| 1279 | |||
| 1280 | /* Queue the GPU write cache flushing we need. */ | ||
| 1281 | if (flush_domains != 0) { | ||
| 1282 | uint32_t seqno; | ||
| 1283 | |||
| 1284 | obj->write_domain &= ~I915_GEM_GPU_DOMAINS; | ||
| 1285 | i915_gem_flush(dev, 0, flush_domains); | ||
| 1286 | seqno = i915_add_request(dev, flush_domains); | ||
| 1287 | i915_gem_object_move_to_active(obj, seqno); | ||
| 1288 | } | ||
| 1289 | |||
| 1290 | /* Wait on any GPU rendering and flushing to occur. */ | ||
| 1291 | if (obj_priv->active) { | ||
| 1292 | int ret; | ||
| 1293 | |||
| 1294 | ret = i915_wait_request(dev, obj_priv->last_rendering_seqno); | ||
| 1295 | if (ret != 0) | ||
| 1296 | return ret; | ||
| 1297 | } | ||
| 1298 | |||
| 1299 | /* If we're writing through the GTT domain, then CPU and GPU caches | ||
| 1300 | * will need to be invalidated at next use. | ||
| 1301 | */ | ||
| 1302 | if (write) | ||
| 1303 | obj->read_domains &= ~(I915_GEM_GPU_DOMAINS | | ||
| 1304 | I915_GEM_DOMAIN_CPU); | ||
| 1305 | |||
| 1306 | /* Flush the CPU domain if it's dirty. */ | ||
| 1307 | if (obj->write_domain & I915_GEM_DOMAIN_CPU) { | ||
| 1308 | i915_gem_clflush_object(obj); | ||
| 1309 | drm_agp_chipset_flush(dev); | ||
| 1310 | |||
| 1311 | obj->write_domain &= ~I915_GEM_DOMAIN_CPU; | ||
| 1312 | } | ||
| 1313 | |||
| 1314 | /* It should now be out of any other write domains, and we can update | ||
| 1315 | * the domain values for our changes. | ||
| 1316 | */ | ||
| 1317 | BUG_ON((obj->write_domain & ~I915_GEM_DOMAIN_GTT) != 0); | ||
| 1318 | obj->read_domains |= I915_GEM_DOMAIN_GTT; | ||
| 1319 | if (write) | ||
| 1320 | obj->write_domain = I915_GEM_DOMAIN_GTT; | ||
| 1321 | |||
| 1322 | return 0; | ||
| 1323 | } | ||
| 1324 | |||
| 1240 | /* | 1325 | /* |
| 1241 | * Set the next domain for the specified object. This | 1326 | * Set the next domain for the specified object. This |
| 1242 | * may not actually perform the necessary flushing/invaliding though, | 1327 | * may not actually perform the necessary flushing/invaliding though, |
| @@ -1634,19 +1719,11 @@ i915_gem_object_pin_and_relocate(struct drm_gem_object *obj, | |||
| 1634 | continue; | 1719 | continue; |
| 1635 | } | 1720 | } |
| 1636 | 1721 | ||
| 1637 | /* Now that we're going to actually write some data in, | 1722 | ret = i915_gem_object_set_to_gtt_domain(obj, 1); |
| 1638 | * make sure that any rendering using this buffer's contents | 1723 | if (ret != 0) { |
| 1639 | * is completed. | 1724 | drm_gem_object_unreference(target_obj); |
| 1640 | */ | 1725 | i915_gem_object_unpin(obj); |
| 1641 | i915_gem_object_wait_rendering(obj); | 1726 | return -EINVAL; |
| 1642 | |||
| 1643 | /* As we're writing through the gtt, flush | ||
| 1644 | * any CPU writes before we write the relocations | ||
| 1645 | */ | ||
| 1646 | if (obj->write_domain & I915_GEM_DOMAIN_CPU) { | ||
| 1647 | i915_gem_clflush_object(obj); | ||
| 1648 | drm_agp_chipset_flush(dev); | ||
| 1649 | obj->write_domain = 0; | ||
| 1650 | } | 1727 | } |
| 1651 | 1728 | ||
| 1652 | /* Map the page containing the relocation we're going to | 1729 | /* Map the page containing the relocation we're going to |
