@@ -376,6 +376,8 @@ struct drm_i915_file_private {
} rps;
unsigned int bsd_ring;
+
+ u32 scheduler_queue_length;
};
/* Used by dp and fdi links */
@@ -1818,6 +1818,10 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
return -EINVAL;
}
+ /* Throttle batch requests per device file */
+ if (i915_scheduler_file_queue_wait(file))
+ return -EAGAIN;
+
/* Copy in the exec list from userland */
exec_list = drm_malloc_ab(sizeof(*exec_list), args->buffer_count);
exec2_list = drm_malloc_ab(sizeof(*exec2_list), args->buffer_count);
@@ -1908,6 +1912,10 @@ i915_gem_execbuffer2(struct drm_device *dev, void *data,
return -EINVAL;
}
+ /* Throttle batch requests per device file */
+ if (i915_scheduler_file_queue_wait(file))
+ return -EAGAIN;
+
exec2_list = drm_malloc_gfp(args->buffer_count,
sizeof(*exec2_list),
GFP_TEMPORARY);
@@ -80,6 +80,7 @@ int i915_scheduler_init(struct drm_device *dev)
scheduler->priority_level_bump = 50;
scheduler->priority_level_preempt = 900;
scheduler->min_flying = 2;
+ scheduler->file_queue_max = 64;
dev_priv->scheduler = scheduler;
@@ -496,6 +497,28 @@ static int i915_scheduler_submit_unlocked(struct intel_engine_cs *engine)
return ret;
}
+/**
+ * i915_scheduler_file_queue_inc - Increment the file's request queue count.
+ * @file: File object to process.
+ */
+static void i915_scheduler_file_queue_inc(struct drm_file *file)
+{
+ struct drm_i915_file_private *file_priv = file->driver_priv;
+
+ file_priv->scheduler_queue_length++;
+}
+
+/**
+ * i915_scheduler_file_queue_dec - Decrement the file's request queue count.
+ * @file: File object to process.
+ */
+static void i915_scheduler_file_queue_dec(struct drm_file *file)
+{
+ struct drm_i915_file_private *file_priv = file->driver_priv;
+
+ file_priv->scheduler_queue_length--;
+}
+
static void i915_generate_dependencies(struct i915_scheduler *scheduler,
struct i915_scheduler_queue_entry *node,
uint32_t engine)
@@ -675,6 +698,8 @@ int i915_scheduler_queue_execbuffer(struct i915_scheduler_queue_entry *qe)
list_add_tail(&node->link, &scheduler->node_queue[engine->id]);
+ i915_scheduler_file_queue_inc(node->params.file);
+
not_flying = i915_scheduler_count_flying(scheduler, engine) <
scheduler->min_flying;
@@ -871,6 +896,12 @@ static bool i915_scheduler_remove(struct i915_scheduler *scheduler,
/* Strip the dependency info while the mutex is still locked */
i915_scheduler_remove_dependent(scheduler, node);
+ /* Likewise clean up the file pointer. */
+ if (node->params.file) {
+ i915_scheduler_file_queue_dec(node->params.file);
+ node->params.file = NULL;
+ }
+
continue;
}
@@ -963,6 +994,92 @@ void i915_scheduler_work_handler(struct work_struct *work)
i915_scheduler_process_work(engine);
}
+/**
+ * i915_scheduler_file_queue_wait - Waits for space in the per file queue.
+ * @file: File object to process.
+ * This allows throttling of applications by limiting the total number of
+ * outstanding requests to a specified level. Once that limit is reached,
+ * this call will stall waiting on the oldest outstanding request. If it can
+ * not stall for any reason it returns true to mean that the queue is full
+ * and no more requests should be accepted.
+ */
+bool i915_scheduler_file_queue_wait(struct drm_file *file)
+{
+ struct drm_i915_file_private *file_priv = file->driver_priv;
+ struct drm_i915_private *dev_priv = file_priv->dev_priv;
+ struct i915_scheduler *scheduler = dev_priv->scheduler;
+ struct drm_i915_gem_request *req;
+ struct i915_scheduler_queue_entry *node;
+ unsigned reset_counter;
+ int ret;
+ struct intel_engine_cs *engine;
+
+ if (file_priv->scheduler_queue_length < scheduler->file_queue_max)
+ return false;
+
+ do {
+ spin_lock_irq(&scheduler->lock);
+
+ /*
+ * Find the first (i.e. oldest) request for this file. In the
+ * case where an app is using multiple engines, this search
+ * might be skewed by engine. However, worst case is an app has
+ * queued ~60 requests to a high indexed engine and then one
+ * request to a low indexed engine. In such a case, the driver
+ * will wait for longer than necessary but operation will
+ * still be correct and that case is not rare enough to add
+ * jiffy based inter-engine checks.
+ */
+ req = NULL;
+ for_each_engine(engine, dev_priv) {
+ for_each_scheduler_node(node, engine->id) {
+ if (I915_SQS_IS_COMPLETE(node))
+ continue;
+
+ if (node->params.file != file)
+ continue;
+
+ req = node->params.request;
+ break;
+ }
+
+ if (req)
+ break;
+ }
+
+ if (!req) {
+ spin_unlock_irq(&scheduler->lock);
+ return false;
+ }
+
+ i915_gem_request_reference(req);
+
+ spin_unlock_irq(&scheduler->lock);
+
+ ret = i915_gem_check_wedge(&dev_priv->gpu_error, false);
+ if (ret)
+ goto err_unref;
+
+ reset_counter = atomic_read(&dev_priv->gpu_error.reset_counter);
+
+ ret = __i915_wait_request(req, reset_counter,
+ I915_WAIT_REQUEST_INTERRUPTIBLE, NULL, NULL);
+ if (ret)
+ goto err_unref;
+
+ /* Make sure the request's resources actually get cleared up */
+ i915_scheduler_process_work(req->engine);
+
+ i915_gem_request_unreference(req);
+ } while(file_priv->scheduler_queue_length >= scheduler->file_queue_max);
+
+ return false;
+
+err_unref:
+ i915_gem_request_unreference(req);
+ return true;
+}
+
static int i915_scheduler_submit_max_priority(struct intel_engine_cs *engine,
bool is_locked)
{
@@ -1185,6 +1302,7 @@ void i915_scheduler_closefile(struct drm_device *dev, struct drm_file *file)
node->status,
engine->name);
+ i915_scheduler_file_queue_dec(node->params.file);
node->params.file = NULL;
}
}
@@ -94,6 +94,7 @@ struct i915_scheduler {
int32_t priority_level_bump;
int32_t priority_level_preempt;
uint32_t min_flying;
+ uint32_t file_queue_max;
};
/* Flag bits for i915_scheduler::flags */
@@ -116,5 +117,6 @@ int i915_scheduler_flush(struct intel_engine_cs *engine, bool is_locked);
int i915_scheduler_flush_stamp(struct intel_engine_cs *engine,
unsigned long stamp, bool is_locked);
bool i915_scheduler_is_mutex_required(struct drm_i915_gem_request *req);
+bool i915_scheduler_file_queue_wait(struct drm_file *file);
#endif /* _I915_SCHEDULER_H_ */