@@ -831,6 +831,56 @@ finish:
return (u32*)addr;
}
+/* Returns a vmap'd pointer to dest_obj, which the caller must unmap */
+static u32 *copy_batch(struct drm_i915_gem_object *dest_obj,
+ struct drm_i915_gem_object *src_obj)
+{
+ int ret = 0;
+ int needs_clflush = 0;
+ u32 *src_addr, *dest_addr = NULL;
+
+ ret = i915_gem_obj_prepare_shmem_read(src_obj, &needs_clflush);
+ if (ret) {
+ DRM_DEBUG_DRIVER("CMD: failed to prep read\n");
+ return ERR_PTR(ret);
+ }
+
+ src_addr = vmap_batch(src_obj);
+ if (!src_addr) {
+ DRM_DEBUG_DRIVER("CMD: Failed to vmap batch\n");
+ ret = -ENOMEM;
+ goto unpin_src;
+ }
+
+ if (needs_clflush)
+ drm_clflush_virt_range((char *)src_addr, src_obj->base.size);
+
+ ret = i915_gem_object_set_to_cpu_domain(dest_obj, true);
+ if (ret) {
+ DRM_DEBUG_DRIVER("CMD: Failed to set batch CPU domain\n");
+ goto unmap_src;
+ }
+
+ dest_addr = vmap_batch(dest_obj);
+ if (!dest_addr) {
+ DRM_DEBUG_DRIVER("CMD: Failed to vmap shadow batch\n");
+ ret = -ENOMEM;
+ goto unmap_src;
+ }
+
+ memcpy(dest_addr, src_addr, src_obj->base.size);
+ if (dest_obj->base.size > src_obj->base.size)
+ memset((u8 *)dest_addr + src_obj->base.size, 0,
+ dest_obj->base.size - src_obj->base.size);
+
+unmap_src:
+ vunmap(src_addr);
+unpin_src:
+ i915_gem_object_unpin_pages(src_obj);
+
+ return ret ? ERR_PTR(ret) : dest_addr;
+}
+
/**
* i915_needs_cmd_parser() - should a given ring use software command parsing?
* @ring: the ring in question
@@ -952,6 +1002,7 @@ static bool check_cmd(const struct intel_engine_cs *ring,
* i915_parse_cmds() - parse a submitted batch buffer for privilege violations
* @ring: the ring on which the batch is to execute
* @batch_obj: the batch buffer in question
+ * @shadow_batch_obj: copy of the batch buffer in question
* @batch_start_offset: byte offset in the batch at which execution starts
* @is_master: is the submitting process the drm master?
*
@@ -962,32 +1013,28 @@ static bool check_cmd(const struct intel_engine_cs *ring,
*/
int i915_parse_cmds(struct intel_engine_cs *ring,
struct drm_i915_gem_object *batch_obj,
+ struct drm_i915_gem_object *shadow_batch_obj,
u32 batch_start_offset,
bool is_master)
{
int ret = 0;
u32 *cmd, *batch_base, *batch_end;
struct drm_i915_cmd_descriptor default_desc = { 0 };
- int needs_clflush = 0;
bool oacontrol_set = false; /* OACONTROL tracking. See check_cmd() */
- ret = i915_gem_obj_prepare_shmem_read(batch_obj, &needs_clflush);
- if (ret) {
- DRM_DEBUG_DRIVER("CMD: failed to prep read\n");
- return ret;
+ batch_base = copy_batch(shadow_batch_obj, batch_obj);
+ if (IS_ERR(batch_base)) {
+ DRM_DEBUG_DRIVER("CMD: Failed to copy batch\n");
+ return PTR_ERR(batch_base);
}
- batch_base = vmap_batch(batch_obj);
- if (!batch_base) {
- DRM_DEBUG_DRIVER("CMD: Failed to vmap batch\n");
- i915_gem_object_unpin_pages(batch_obj);
- return -ENOMEM;
- }
-
- if (needs_clflush)
- drm_clflush_virt_range((char *)batch_base, batch_obj->base.size);
-
cmd = batch_base + (batch_start_offset / sizeof(*cmd));
+
+ /*
+ * We use the source object's size because the shadow object is as
+ * large or larger and copy_batch() will write MI_NOPs to the extra
+ * space. Parsing should be faster in some cases this way.
+ */
batch_end = cmd + (batch_obj->base.size / sizeof(*batch_end));
while (cmd < batch_end) {
@@ -1039,7 +1086,12 @@ int i915_parse_cmds(struct intel_engine_cs *ring,
vunmap(batch_base);
- i915_gem_object_unpin_pages(batch_obj);
+ if (!ret) {
+ ret = i915_gem_object_set_to_gtt_domain(shadow_batch_obj,
+ false);
+ if (ret)
+ DRM_DEBUG_DRIVER("CMD: Failed to set batch GTT domain\n");
+ }
return ret;
}
@@ -1877,6 +1877,7 @@ int i915_driver_unload(struct drm_device *dev)
mutex_lock(&dev->struct_mutex);
i915_gem_cleanup_ringbuffer(dev);
+ i915_gem_batch_pool_fini(&dev_priv->mm.batch_pool);
i915_gem_context_fini(dev);
WARN_ON(dev_priv->mm.aliasing_ppgtt);
mutex_unlock(&dev->struct_mutex);
@@ -1081,6 +1081,13 @@ struct i915_gem_mm {
*/
struct list_head unbound_list;
+ /*
+ * A pool of objects to use as shadow copies of client batch buffers
+ * when the command parser is enabled. Prevents the client from
+ * modifying the batch contents after software parsing.
+ */
+ struct i915_gem_batch_pool batch_pool;
+
/** Usable portion of the GTT for GEM */
unsigned long stolen_base; /* limited to low memory (32-bit) */
@@ -2618,6 +2625,7 @@ void i915_cmd_parser_fini_ring(struct intel_engine_cs *ring);
bool i915_needs_cmd_parser(struct intel_engine_cs *ring);
int i915_parse_cmds(struct intel_engine_cs *ring,
struct drm_i915_gem_object *batch_obj,
+ struct drm_i915_gem_object *shadow_batch_obj,
u32 batch_start_offset,
bool is_master);
@@ -2476,6 +2476,13 @@ static void i915_gem_free_request(struct drm_i915_gem_request *request)
if (request->ctx)
i915_gem_context_unreference(request->ctx);
+ if (!list_empty(&request->batch_obj->batch_pool_list)) {
+ struct drm_i915_private *dev_priv = to_i915(request->ring->dev);
+
+ i915_gem_batch_pool_put(&dev_priv->mm.batch_pool,
+ request->batch_obj);
+ }
+
kfree(request);
}
@@ -4947,6 +4954,8 @@ i915_gem_load(struct drm_device *dev)
dev_priv->mm.oom_notifier.notifier_call = i915_gem_shrinker_oom;
register_oom_notifier(&dev_priv->mm.oom_notifier);
+ i915_gem_batch_pool_init(dev, &dev_priv->mm.batch_pool);
+
mutex_init(&dev_priv->fb_tracking.lock);
}
@@ -1242,6 +1242,8 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
struct drm_i915_private *dev_priv = dev->dev_private;
struct eb_vmas *eb;
struct drm_i915_gem_object *batch_obj;
+ struct drm_i915_gem_object *shadow_batch_obj = NULL;
+ struct drm_i915_gem_exec_object2 shadow_exec_entry;
struct intel_engine_cs *ring;
struct intel_context *ctx;
struct i915_address_space *vm;
@@ -1366,13 +1368,38 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
batch_obj->base.pending_read_domains |= I915_GEM_DOMAIN_COMMAND;
if (i915_needs_cmd_parser(ring)) {
+ struct i915_vma *vma;
+
+ shadow_batch_obj =
+ i915_gem_batch_pool_get(&dev_priv->mm.batch_pool,
+ batch_obj->base.size);
+ if (IS_ERR(shadow_batch_obj)) {
+ ret = PTR_ERR(shadow_batch_obj);
+ /* Don't try to clean up the obj in the error path */
+ shadow_batch_obj = NULL;
+ goto err;
+ }
+
+ ret = i915_gem_obj_ggtt_pin(shadow_batch_obj, 4096, 0);
+ if (ret)
+ goto err;
+
ret = i915_parse_cmds(ring,
batch_obj,
+ shadow_batch_obj,
args->batch_start_offset,
file->is_master);
if (ret)
goto err;
+ vma = i915_gem_obj_to_ggtt(shadow_batch_obj);
+ vma->exec_entry = &shadow_exec_entry;
+ vma->exec_entry->flags = __EXEC_OBJECT_HAS_PIN;
+ drm_gem_object_reference(&shadow_batch_obj->base);
+ list_add_tail(&vma->exec_list, &eb->vmas);
+
+ batch_obj = shadow_batch_obj;
+
/*
* XXX: Actually do this when enabling batch copy...
*
@@ -1410,6 +1437,14 @@ err:
i915_gem_context_unreference(ctx);
eb_destroy(eb);
+ if (ret && ring && shadow_batch_obj) {
+ if (i915_gem_obj_is_pinned(shadow_batch_obj))
+ i915_gem_object_ggtt_unpin(shadow_batch_obj);
+
+ i915_gem_batch_pool_put(&dev_priv->mm.batch_pool,
+ shadow_batch_obj);
+ }
+
mutex_unlock(&dev->struct_mutex);
pre_mutex_err: