|
@@ -41,7 +41,8 @@ static void i915_gem_object_flush_gtt_write_domain(struct drm_i915_gem_object *o
|
|
static void i915_gem_object_flush_cpu_write_domain(struct drm_i915_gem_object *obj);
|
|
static void i915_gem_object_flush_cpu_write_domain(struct drm_i915_gem_object *obj);
|
|
static __must_check int i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj,
|
|
static __must_check int i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj,
|
|
unsigned alignment,
|
|
unsigned alignment,
|
|
- bool map_and_fenceable);
|
|
|
|
|
|
+ bool map_and_fenceable,
|
|
|
|
+ bool nonblocking);
|
|
static int i915_gem_phys_pwrite(struct drm_device *dev,
|
|
static int i915_gem_phys_pwrite(struct drm_device *dev,
|
|
struct drm_i915_gem_object *obj,
|
|
struct drm_i915_gem_object *obj,
|
|
struct drm_i915_gem_pwrite *args,
|
|
struct drm_i915_gem_pwrite *args,
|
|
@@ -55,6 +56,8 @@ static void i915_gem_object_update_fence(struct drm_i915_gem_object *obj,
|
|
|
|
|
|
static int i915_gem_inactive_shrink(struct shrinker *shrinker,
|
|
static int i915_gem_inactive_shrink(struct shrinker *shrinker,
|
|
struct shrink_control *sc);
|
|
struct shrink_control *sc);
|
|
|
|
+static long i915_gem_purge(struct drm_i915_private *dev_priv, long target);
|
|
|
|
+static void i915_gem_shrink_all(struct drm_i915_private *dev_priv);
|
|
static void i915_gem_object_truncate(struct drm_i915_gem_object *obj);
|
|
static void i915_gem_object_truncate(struct drm_i915_gem_object *obj);
|
|
|
|
|
|
static inline void i915_gem_object_fence_lost(struct drm_i915_gem_object *obj)
|
|
static inline void i915_gem_object_fence_lost(struct drm_i915_gem_object *obj)
|
|
@@ -140,7 +143,7 @@ int i915_mutex_lock_interruptible(struct drm_device *dev)
|
|
static inline bool
|
|
static inline bool
|
|
i915_gem_object_is_inactive(struct drm_i915_gem_object *obj)
|
|
i915_gem_object_is_inactive(struct drm_i915_gem_object *obj)
|
|
{
|
|
{
|
|
- return !obj->active;
|
|
|
|
|
|
+ return obj->gtt_space && !obj->active;
|
|
}
|
|
}
|
|
|
|
|
|
int
|
|
int
|
|
@@ -179,7 +182,7 @@ i915_gem_get_aperture_ioctl(struct drm_device *dev, void *data,
|
|
|
|
|
|
pinned = 0;
|
|
pinned = 0;
|
|
mutex_lock(&dev->struct_mutex);
|
|
mutex_lock(&dev->struct_mutex);
|
|
- list_for_each_entry(obj, &dev_priv->mm.gtt_list, gtt_list)
|
|
|
|
|
|
+ list_for_each_entry(obj, &dev_priv->mm.bound_list, gtt_list)
|
|
if (obj->pin_count)
|
|
if (obj->pin_count)
|
|
pinned += obj->gtt_space->size;
|
|
pinned += obj->gtt_space->size;
|
|
mutex_unlock(&dev->struct_mutex);
|
|
mutex_unlock(&dev->struct_mutex);
|
|
@@ -423,9 +426,11 @@ i915_gem_shmem_pread(struct drm_device *dev,
|
|
* anyway again before the next pread happens. */
|
|
* anyway again before the next pread happens. */
|
|
if (obj->cache_level == I915_CACHE_NONE)
|
|
if (obj->cache_level == I915_CACHE_NONE)
|
|
needs_clflush = 1;
|
|
needs_clflush = 1;
|
|
- ret = i915_gem_object_set_to_gtt_domain(obj, false);
|
|
|
|
- if (ret)
|
|
|
|
- return ret;
|
|
|
|
|
|
+ if (obj->gtt_space) {
|
|
|
|
+ ret = i915_gem_object_set_to_gtt_domain(obj, false);
|
|
|
|
+ if (ret)
|
|
|
|
+ return ret;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
offset = args->offset;
|
|
offset = args->offset;
|
|
@@ -605,7 +610,7 @@ i915_gem_gtt_pwrite_fast(struct drm_device *dev,
|
|
char __user *user_data;
|
|
char __user *user_data;
|
|
int page_offset, page_length, ret;
|
|
int page_offset, page_length, ret;
|
|
|
|
|
|
- ret = i915_gem_object_pin(obj, 0, true);
|
|
|
|
|
|
+ ret = i915_gem_object_pin(obj, 0, true, true);
|
|
if (ret)
|
|
if (ret)
|
|
goto out;
|
|
goto out;
|
|
|
|
|
|
@@ -751,9 +756,11 @@ i915_gem_shmem_pwrite(struct drm_device *dev,
|
|
* right away and we therefore have to clflush anyway. */
|
|
* right away and we therefore have to clflush anyway. */
|
|
if (obj->cache_level == I915_CACHE_NONE)
|
|
if (obj->cache_level == I915_CACHE_NONE)
|
|
needs_clflush_after = 1;
|
|
needs_clflush_after = 1;
|
|
- ret = i915_gem_object_set_to_gtt_domain(obj, true);
|
|
|
|
- if (ret)
|
|
|
|
- return ret;
|
|
|
|
|
|
+ if (obj->gtt_space) {
|
|
|
|
+ ret = i915_gem_object_set_to_gtt_domain(obj, true);
|
|
|
|
+ if (ret)
|
|
|
|
+ return ret;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
/* Same trick applies for invalidate partially written cachelines before
|
|
/* Same trick applies for invalidate partially written cachelines before
|
|
* writing. */
|
|
* writing. */
|
|
@@ -919,10 +926,8 @@ i915_gem_pwrite_ioctl(struct drm_device *dev, void *data,
|
|
goto out;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
|
|
- if (obj->gtt_space &&
|
|
|
|
- obj->cache_level == I915_CACHE_NONE &&
|
|
|
|
|
|
+ if (obj->cache_level == I915_CACHE_NONE &&
|
|
obj->tiling_mode == I915_TILING_NONE &&
|
|
obj->tiling_mode == I915_TILING_NONE &&
|
|
- obj->map_and_fenceable &&
|
|
|
|
obj->base.write_domain != I915_GEM_DOMAIN_CPU) {
|
|
obj->base.write_domain != I915_GEM_DOMAIN_CPU) {
|
|
ret = i915_gem_gtt_pwrite_fast(dev, obj, args, file);
|
|
ret = i915_gem_gtt_pwrite_fast(dev, obj, args, file);
|
|
/* Note that the gtt paths might fail with non-page-backed user
|
|
/* Note that the gtt paths might fail with non-page-backed user
|
|
@@ -930,7 +935,7 @@ i915_gem_pwrite_ioctl(struct drm_device *dev, void *data,
|
|
* textures). Fallback to the shmem path in that case. */
|
|
* textures). Fallback to the shmem path in that case. */
|
|
}
|
|
}
|
|
|
|
|
|
- if (ret == -EFAULT)
|
|
|
|
|
|
+ if (ret == -EFAULT || ret == -ENOSPC)
|
|
ret = i915_gem_shmem_pwrite(dev, obj, args, file);
|
|
ret = i915_gem_shmem_pwrite(dev, obj, args, file);
|
|
|
|
|
|
out:
|
|
out:
|
|
@@ -940,6 +945,240 @@ unlock:
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+int
|
|
|
|
+i915_gem_check_wedge(struct drm_i915_private *dev_priv,
|
|
|
|
+ bool interruptible)
|
|
|
|
+{
|
|
|
|
+ if (atomic_read(&dev_priv->mm.wedged)) {
|
|
|
|
+ struct completion *x = &dev_priv->error_completion;
|
|
|
|
+ bool recovery_complete;
|
|
|
|
+ unsigned long flags;
|
|
|
|
+
|
|
|
|
+ /* Give the error handler a chance to run. */
|
|
|
|
+ spin_lock_irqsave(&x->wait.lock, flags);
|
|
|
|
+ recovery_complete = x->done > 0;
|
|
|
|
+ spin_unlock_irqrestore(&x->wait.lock, flags);
|
|
|
|
+
|
|
|
|
+ /* Non-interruptible callers can't handle -EAGAIN, hence return
|
|
|
|
+ * -EIO unconditionally for these. */
|
|
|
|
+ if (!interruptible)
|
|
|
|
+ return -EIO;
|
|
|
|
+
|
|
|
|
+ /* Recovery complete, but still wedged means reset failure. */
|
|
|
|
+ if (recovery_complete)
|
|
|
|
+ return -EIO;
|
|
|
|
+
|
|
|
|
+ return -EAGAIN;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * Compare seqno against outstanding lazy request. Emit a request if they are
|
|
|
|
+ * equal.
|
|
|
|
+ */
|
|
|
|
+static int
|
|
|
|
+i915_gem_check_olr(struct intel_ring_buffer *ring, u32 seqno)
|
|
|
|
+{
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ BUG_ON(!mutex_is_locked(&ring->dev->struct_mutex));
|
|
|
|
+
|
|
|
|
+ ret = 0;
|
|
|
|
+ if (seqno == ring->outstanding_lazy_request)
|
|
|
|
+ ret = i915_add_request(ring, NULL, NULL);
|
|
|
|
+
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * __wait_seqno - wait until execution of seqno has finished
|
|
|
|
+ * @ring: the ring expected to report seqno
|
|
|
|
+ * @seqno: duh!
|
|
|
|
+ * @interruptible: do an interruptible wait (normally yes)
|
|
|
|
+ * @timeout: in - how long to wait (NULL forever); out - how much time remaining
|
|
|
|
+ *
|
|
|
|
+ * Returns 0 if the seqno was found within the alloted time. Else returns the
|
|
|
|
+ * errno with remaining time filled in timeout argument.
|
|
|
|
+ */
|
|
|
|
+static int __wait_seqno(struct intel_ring_buffer *ring, u32 seqno,
|
|
|
|
+ bool interruptible, struct timespec *timeout)
|
|
|
|
+{
|
|
|
|
+ drm_i915_private_t *dev_priv = ring->dev->dev_private;
|
|
|
|
+ struct timespec before, now, wait_time={1,0};
|
|
|
|
+ unsigned long timeout_jiffies;
|
|
|
|
+ long end;
|
|
|
|
+ bool wait_forever = true;
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ if (i915_seqno_passed(ring->get_seqno(ring, true), seqno))
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ trace_i915_gem_request_wait_begin(ring, seqno);
|
|
|
|
+
|
|
|
|
+ if (timeout != NULL) {
|
|
|
|
+ wait_time = *timeout;
|
|
|
|
+ wait_forever = false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ timeout_jiffies = timespec_to_jiffies(&wait_time);
|
|
|
|
+
|
|
|
|
+ if (WARN_ON(!ring->irq_get(ring)))
|
|
|
|
+ return -ENODEV;
|
|
|
|
+
|
|
|
|
+ /* Record current time in case interrupted by signal, or wedged * */
|
|
|
|
+ getrawmonotonic(&before);
|
|
|
|
+
|
|
|
|
+#define EXIT_COND \
|
|
|
|
+ (i915_seqno_passed(ring->get_seqno(ring, false), seqno) || \
|
|
|
|
+ atomic_read(&dev_priv->mm.wedged))
|
|
|
|
+ do {
|
|
|
|
+ if (interruptible)
|
|
|
|
+ end = wait_event_interruptible_timeout(ring->irq_queue,
|
|
|
|
+ EXIT_COND,
|
|
|
|
+ timeout_jiffies);
|
|
|
|
+ else
|
|
|
|
+ end = wait_event_timeout(ring->irq_queue, EXIT_COND,
|
|
|
|
+ timeout_jiffies);
|
|
|
|
+
|
|
|
|
+ ret = i915_gem_check_wedge(dev_priv, interruptible);
|
|
|
|
+ if (ret)
|
|
|
|
+ end = ret;
|
|
|
|
+ } while (end == 0 && wait_forever);
|
|
|
|
+
|
|
|
|
+ getrawmonotonic(&now);
|
|
|
|
+
|
|
|
|
+ ring->irq_put(ring);
|
|
|
|
+ trace_i915_gem_request_wait_end(ring, seqno);
|
|
|
|
+#undef EXIT_COND
|
|
|
|
+
|
|
|
|
+ if (timeout) {
|
|
|
|
+ struct timespec sleep_time = timespec_sub(now, before);
|
|
|
|
+ *timeout = timespec_sub(*timeout, sleep_time);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ switch (end) {
|
|
|
|
+ case -EIO:
|
|
|
|
+ case -EAGAIN: /* Wedged */
|
|
|
|
+ case -ERESTARTSYS: /* Signal */
|
|
|
|
+ return (int)end;
|
|
|
|
+ case 0: /* Timeout */
|
|
|
|
+ if (timeout)
|
|
|
|
+ set_normalized_timespec(timeout, 0, 0);
|
|
|
|
+ return -ETIME;
|
|
|
|
+ default: /* Completed */
|
|
|
|
+ WARN_ON(end < 0); /* We're not aware of other errors */
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * Waits for a sequence number to be signaled, and cleans up the
|
|
|
|
+ * request and object lists appropriately for that event.
|
|
|
|
+ */
|
|
|
|
+int
|
|
|
|
+i915_wait_seqno(struct intel_ring_buffer *ring, uint32_t seqno)
|
|
|
|
+{
|
|
|
|
+ struct drm_device *dev = ring->dev;
|
|
|
|
+ struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
|
+ bool interruptible = dev_priv->mm.interruptible;
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ BUG_ON(!mutex_is_locked(&dev->struct_mutex));
|
|
|
|
+ BUG_ON(seqno == 0);
|
|
|
|
+
|
|
|
|
+ ret = i915_gem_check_wedge(dev_priv, interruptible);
|
|
|
|
+ if (ret)
|
|
|
|
+ return ret;
|
|
|
|
+
|
|
|
|
+ ret = i915_gem_check_olr(ring, seqno);
|
|
|
|
+ if (ret)
|
|
|
|
+ return ret;
|
|
|
|
+
|
|
|
|
+ return __wait_seqno(ring, seqno, interruptible, NULL);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * Ensures that all rendering to the object has completed and the object is
|
|
|
|
+ * safe to unbind from the GTT or access from the CPU.
|
|
|
|
+ */
|
|
|
|
+static __must_check int
|
|
|
|
+i915_gem_object_wait_rendering(struct drm_i915_gem_object *obj,
|
|
|
|
+ bool readonly)
|
|
|
|
+{
|
|
|
|
+ struct intel_ring_buffer *ring = obj->ring;
|
|
|
|
+ u32 seqno;
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ seqno = readonly ? obj->last_write_seqno : obj->last_read_seqno;
|
|
|
|
+ if (seqno == 0)
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ ret = i915_wait_seqno(ring, seqno);
|
|
|
|
+ if (ret)
|
|
|
|
+ return ret;
|
|
|
|
+
|
|
|
|
+ i915_gem_retire_requests_ring(ring);
|
|
|
|
+
|
|
|
|
+ /* Manually manage the write flush as we may have not yet
|
|
|
|
+ * retired the buffer.
|
|
|
|
+ */
|
|
|
|
+ if (obj->last_write_seqno &&
|
|
|
|
+ i915_seqno_passed(seqno, obj->last_write_seqno)) {
|
|
|
|
+ obj->last_write_seqno = 0;
|
|
|
|
+ obj->base.write_domain &= ~I915_GEM_GPU_DOMAINS;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* A nonblocking variant of the above wait. This is a highly dangerous routine
|
|
|
|
+ * as the object state may change during this call.
|
|
|
|
+ */
|
|
|
|
+static __must_check int
|
|
|
|
+i915_gem_object_wait_rendering__nonblocking(struct drm_i915_gem_object *obj,
|
|
|
|
+ bool readonly)
|
|
|
|
+{
|
|
|
|
+ struct drm_device *dev = obj->base.dev;
|
|
|
|
+ struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
|
+ struct intel_ring_buffer *ring = obj->ring;
|
|
|
|
+ u32 seqno;
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ BUG_ON(!mutex_is_locked(&dev->struct_mutex));
|
|
|
|
+ BUG_ON(!dev_priv->mm.interruptible);
|
|
|
|
+
|
|
|
|
+ seqno = readonly ? obj->last_write_seqno : obj->last_read_seqno;
|
|
|
|
+ if (seqno == 0)
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ ret = i915_gem_check_wedge(dev_priv, true);
|
|
|
|
+ if (ret)
|
|
|
|
+ return ret;
|
|
|
|
+
|
|
|
|
+ ret = i915_gem_check_olr(ring, seqno);
|
|
|
|
+ if (ret)
|
|
|
|
+ return ret;
|
|
|
|
+
|
|
|
|
+ mutex_unlock(&dev->struct_mutex);
|
|
|
|
+ ret = __wait_seqno(ring, seqno, true, NULL);
|
|
|
|
+ mutex_lock(&dev->struct_mutex);
|
|
|
|
+
|
|
|
|
+ i915_gem_retire_requests_ring(ring);
|
|
|
|
+
|
|
|
|
+ /* Manually manage the write flush as we may have not yet
|
|
|
|
+ * retired the buffer.
|
|
|
|
+ */
|
|
|
|
+ if (obj->last_write_seqno &&
|
|
|
|
+ i915_seqno_passed(seqno, obj->last_write_seqno)) {
|
|
|
|
+ obj->last_write_seqno = 0;
|
|
|
|
+ obj->base.write_domain &= ~I915_GEM_GPU_DOMAINS;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* Called when user space prepares to use an object with the CPU, either
|
|
* Called when user space prepares to use an object with the CPU, either
|
|
* through the mmap ioctl's mapping or a GTT mapping.
|
|
* through the mmap ioctl's mapping or a GTT mapping.
|
|
@@ -977,6 +1216,14 @@ i915_gem_set_domain_ioctl(struct drm_device *dev, void *data,
|
|
goto unlock;
|
|
goto unlock;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /* Try to flush the object off the GPU without holding the lock.
|
|
|
|
+ * We will repeat the flush holding the lock in the normal manner
|
|
|
|
+ * to catch cases where we are gazumped.
|
|
|
|
+ */
|
|
|
|
+ ret = i915_gem_object_wait_rendering__nonblocking(obj, !write_domain);
|
|
|
|
+ if (ret)
|
|
|
|
+ goto unref;
|
|
|
|
+
|
|
if (read_domains & I915_GEM_DOMAIN_GTT) {
|
|
if (read_domains & I915_GEM_DOMAIN_GTT) {
|
|
ret = i915_gem_object_set_to_gtt_domain(obj, write_domain != 0);
|
|
ret = i915_gem_object_set_to_gtt_domain(obj, write_domain != 0);
|
|
|
|
|
|
@@ -990,6 +1237,7 @@ i915_gem_set_domain_ioctl(struct drm_device *dev, void *data,
|
|
ret = i915_gem_object_set_to_cpu_domain(obj, write_domain != 0);
|
|
ret = i915_gem_object_set_to_cpu_domain(obj, write_domain != 0);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+unref:
|
|
drm_gem_object_unreference(&obj->base);
|
|
drm_gem_object_unreference(&obj->base);
|
|
unlock:
|
|
unlock:
|
|
mutex_unlock(&dev->struct_mutex);
|
|
mutex_unlock(&dev->struct_mutex);
|
|
@@ -1109,7 +1357,7 @@ int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
|
|
goto unlock;
|
|
goto unlock;
|
|
}
|
|
}
|
|
if (!obj->gtt_space) {
|
|
if (!obj->gtt_space) {
|
|
- ret = i915_gem_object_bind_to_gtt(obj, 0, true);
|
|
|
|
|
|
+ ret = i915_gem_object_bind_to_gtt(obj, 0, true, false);
|
|
if (ret)
|
|
if (ret)
|
|
goto unlock;
|
|
goto unlock;
|
|
|
|
|
|
@@ -1270,6 +1518,42 @@ i915_gem_get_unfenced_gtt_alignment(struct drm_device *dev,
|
|
return i915_gem_get_gtt_size(dev, size, tiling_mode);
|
|
return i915_gem_get_gtt_size(dev, size, tiling_mode);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int i915_gem_object_create_mmap_offset(struct drm_i915_gem_object *obj)
|
|
|
|
+{
|
|
|
|
+ struct drm_i915_private *dev_priv = obj->base.dev->dev_private;
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ if (obj->base.map_list.map)
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ ret = drm_gem_create_mmap_offset(&obj->base);
|
|
|
|
+ if (ret != -ENOSPC)
|
|
|
|
+ return ret;
|
|
|
|
+
|
|
|
|
+ /* Badly fragmented mmap space? The only way we can recover
|
|
|
|
+ * space is by destroying unwanted objects. We can't randomly release
|
|
|
|
+ * mmap_offsets as userspace expects them to be persistent for the
|
|
|
|
+ * lifetime of the objects. The closest we can is to release the
|
|
|
|
+ * offsets on purgeable objects by truncating it and marking it purged,
|
|
|
|
+ * which prevents userspace from ever using that object again.
|
|
|
|
+ */
|
|
|
|
+ i915_gem_purge(dev_priv, obj->base.size >> PAGE_SHIFT);
|
|
|
|
+ ret = drm_gem_create_mmap_offset(&obj->base);
|
|
|
|
+ if (ret != -ENOSPC)
|
|
|
|
+ return ret;
|
|
|
|
+
|
|
|
|
+ i915_gem_shrink_all(dev_priv);
|
|
|
|
+ return drm_gem_create_mmap_offset(&obj->base);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void i915_gem_object_free_mmap_offset(struct drm_i915_gem_object *obj)
|
|
|
|
+{
|
|
|
|
+ if (!obj->base.map_list.map)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ drm_gem_free_mmap_offset(&obj->base);
|
|
|
|
+}
|
|
|
|
+
|
|
int
|
|
int
|
|
i915_gem_mmap_gtt(struct drm_file *file,
|
|
i915_gem_mmap_gtt(struct drm_file *file,
|
|
struct drm_device *dev,
|
|
struct drm_device *dev,
|
|
@@ -1301,11 +1585,9 @@ i915_gem_mmap_gtt(struct drm_file *file,
|
|
goto out;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
|
|
- if (!obj->base.map_list.map) {
|
|
|
|
- ret = drm_gem_create_mmap_offset(&obj->base);
|
|
|
|
- if (ret)
|
|
|
|
- goto out;
|
|
|
|
- }
|
|
|
|
|
|
+ ret = i915_gem_object_create_mmap_offset(obj);
|
|
|
|
+ if (ret)
|
|
|
|
+ goto out;
|
|
|
|
|
|
*offset = (u64)obj->base.map_list.hash.key << PAGE_SHIFT;
|
|
*offset = (u64)obj->base.map_list.hash.key << PAGE_SHIFT;
|
|
|
|
|
|
@@ -1340,64 +1622,58 @@ i915_gem_mmap_gtt_ioctl(struct drm_device *dev, void *data,
|
|
return i915_gem_mmap_gtt(file, dev, args->handle, &args->offset);
|
|
return i915_gem_mmap_gtt(file, dev, args->handle, &args->offset);
|
|
}
|
|
}
|
|
|
|
|
|
-int
|
|
|
|
-i915_gem_object_get_pages_gtt(struct drm_i915_gem_object *obj,
|
|
|
|
- gfp_t gfpmask)
|
|
|
|
|
|
+/* Immediately discard the backing storage */
|
|
|
|
+static void
|
|
|
|
+i915_gem_object_truncate(struct drm_i915_gem_object *obj)
|
|
{
|
|
{
|
|
- int page_count, i;
|
|
|
|
- struct address_space *mapping;
|
|
|
|
struct inode *inode;
|
|
struct inode *inode;
|
|
- struct page *page;
|
|
|
|
|
|
|
|
- if (obj->pages || obj->sg_table)
|
|
|
|
- return 0;
|
|
|
|
|
|
+ i915_gem_object_free_mmap_offset(obj);
|
|
|
|
|
|
- /* Get the list of pages out of our struct file. They'll be pinned
|
|
|
|
- * at this point until we release them.
|
|
|
|
- */
|
|
|
|
- page_count = obj->base.size / PAGE_SIZE;
|
|
|
|
- BUG_ON(obj->pages != NULL);
|
|
|
|
- obj->pages = drm_malloc_ab(page_count, sizeof(struct page *));
|
|
|
|
- if (obj->pages == NULL)
|
|
|
|
- return -ENOMEM;
|
|
|
|
|
|
+ if (obj->base.filp == NULL)
|
|
|
|
+ return;
|
|
|
|
|
|
|
|
+ /* Our goal here is to return as much of the memory as
|
|
|
|
+ * is possible back to the system as we are called from OOM.
|
|
|
|
+ * To do this we must instruct the shmfs to drop all of its
|
|
|
|
+ * backing pages, *now*.
|
|
|
|
+ */
|
|
inode = obj->base.filp->f_path.dentry->d_inode;
|
|
inode = obj->base.filp->f_path.dentry->d_inode;
|
|
- mapping = inode->i_mapping;
|
|
|
|
- gfpmask |= mapping_gfp_mask(mapping);
|
|
|
|
-
|
|
|
|
- for (i = 0; i < page_count; i++) {
|
|
|
|
- page = shmem_read_mapping_page_gfp(mapping, i, gfpmask);
|
|
|
|
- if (IS_ERR(page))
|
|
|
|
- goto err_pages;
|
|
|
|
-
|
|
|
|
- obj->pages[i] = page;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (i915_gem_object_needs_bit17_swizzle(obj))
|
|
|
|
- i915_gem_object_do_bit_17_swizzle(obj);
|
|
|
|
-
|
|
|
|
- return 0;
|
|
|
|
|
|
+ shmem_truncate_range(inode, 0, (loff_t)-1);
|
|
|
|
|
|
-err_pages:
|
|
|
|
- while (i--)
|
|
|
|
- page_cache_release(obj->pages[i]);
|
|
|
|
|
|
+ obj->madv = __I915_MADV_PURGED;
|
|
|
|
+}
|
|
|
|
|
|
- drm_free_large(obj->pages);
|
|
|
|
- obj->pages = NULL;
|
|
|
|
- return PTR_ERR(page);
|
|
|
|
|
|
+static inline int
|
|
|
|
+i915_gem_object_is_purgeable(struct drm_i915_gem_object *obj)
|
|
|
|
+{
|
|
|
|
+ return obj->madv == I915_MADV_DONTNEED;
|
|
}
|
|
}
|
|
|
|
|
|
-static void
|
|
|
|
|
|
+static int
|
|
i915_gem_object_put_pages_gtt(struct drm_i915_gem_object *obj)
|
|
i915_gem_object_put_pages_gtt(struct drm_i915_gem_object *obj)
|
|
{
|
|
{
|
|
int page_count = obj->base.size / PAGE_SIZE;
|
|
int page_count = obj->base.size / PAGE_SIZE;
|
|
- int i;
|
|
|
|
|
|
+ int ret, i;
|
|
|
|
|
|
- if (!obj->pages)
|
|
|
|
- return;
|
|
|
|
|
|
+ BUG_ON(obj->gtt_space);
|
|
|
|
|
|
|
|
+ if (obj->pages == NULL)
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ BUG_ON(obj->gtt_space);
|
|
BUG_ON(obj->madv == __I915_MADV_PURGED);
|
|
BUG_ON(obj->madv == __I915_MADV_PURGED);
|
|
|
|
|
|
|
|
+ ret = i915_gem_object_set_to_cpu_domain(obj, true);
|
|
|
|
+ if (ret) {
|
|
|
|
+ /* In the event of a disaster, abandon all caches and
|
|
|
|
+ * hope for the best.
|
|
|
|
+ */
|
|
|
|
+ WARN_ON(ret != -EIO);
|
|
|
|
+ i915_gem_clflush_object(obj);
|
|
|
|
+ obj->base.read_domains = obj->base.write_domain = I915_GEM_DOMAIN_CPU;
|
|
|
|
+ }
|
|
|
|
+
|
|
if (i915_gem_object_needs_bit17_swizzle(obj))
|
|
if (i915_gem_object_needs_bit17_swizzle(obj))
|
|
i915_gem_object_save_bit_17_swizzle(obj);
|
|
i915_gem_object_save_bit_17_swizzle(obj);
|
|
|
|
|
|
@@ -1417,6 +1693,129 @@ i915_gem_object_put_pages_gtt(struct drm_i915_gem_object *obj)
|
|
|
|
|
|
drm_free_large(obj->pages);
|
|
drm_free_large(obj->pages);
|
|
obj->pages = NULL;
|
|
obj->pages = NULL;
|
|
|
|
+
|
|
|
|
+ list_del(&obj->gtt_list);
|
|
|
|
+
|
|
|
|
+ if (i915_gem_object_is_purgeable(obj))
|
|
|
|
+ i915_gem_object_truncate(obj);
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static long
|
|
|
|
+i915_gem_purge(struct drm_i915_private *dev_priv, long target)
|
|
|
|
+{
|
|
|
|
+ struct drm_i915_gem_object *obj, *next;
|
|
|
|
+ long count = 0;
|
|
|
|
+
|
|
|
|
+ list_for_each_entry_safe(obj, next,
|
|
|
|
+ &dev_priv->mm.unbound_list,
|
|
|
|
+ gtt_list) {
|
|
|
|
+ if (i915_gem_object_is_purgeable(obj) &&
|
|
|
|
+ i915_gem_object_put_pages_gtt(obj) == 0) {
|
|
|
|
+ count += obj->base.size >> PAGE_SHIFT;
|
|
|
|
+ if (count >= target)
|
|
|
|
+ return count;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ list_for_each_entry_safe(obj, next,
|
|
|
|
+ &dev_priv->mm.inactive_list,
|
|
|
|
+ mm_list) {
|
|
|
|
+ if (i915_gem_object_is_purgeable(obj) &&
|
|
|
|
+ i915_gem_object_unbind(obj) == 0 &&
|
|
|
|
+ i915_gem_object_put_pages_gtt(obj) == 0) {
|
|
|
|
+ count += obj->base.size >> PAGE_SHIFT;
|
|
|
|
+ if (count >= target)
|
|
|
|
+ return count;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return count;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+i915_gem_shrink_all(struct drm_i915_private *dev_priv)
|
|
|
|
+{
|
|
|
|
+ struct drm_i915_gem_object *obj, *next;
|
|
|
|
+
|
|
|
|
+ i915_gem_evict_everything(dev_priv->dev);
|
|
|
|
+
|
|
|
|
+ list_for_each_entry_safe(obj, next, &dev_priv->mm.unbound_list, gtt_list)
|
|
|
|
+ i915_gem_object_put_pages_gtt(obj);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+int
|
|
|
|
+i915_gem_object_get_pages_gtt(struct drm_i915_gem_object *obj)
|
|
|
|
+{
|
|
|
|
+ struct drm_i915_private *dev_priv = obj->base.dev->dev_private;
|
|
|
|
+ int page_count, i;
|
|
|
|
+ struct address_space *mapping;
|
|
|
|
+ struct page *page;
|
|
|
|
+ gfp_t gfp;
|
|
|
|
+
|
|
|
|
+ if (obj->pages || obj->sg_table)
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ /* Assert that the object is not currently in any GPU domain. As it
|
|
|
|
+ * wasn't in the GTT, there shouldn't be any way it could have been in
|
|
|
|
+ * a GPU cache
|
|
|
|
+ */
|
|
|
|
+ BUG_ON(obj->base.read_domains & I915_GEM_GPU_DOMAINS);
|
|
|
|
+ BUG_ON(obj->base.write_domain & I915_GEM_GPU_DOMAINS);
|
|
|
|
+
|
|
|
|
+ /* Get the list of pages out of our struct file. They'll be pinned
|
|
|
|
+ * at this point until we release them.
|
|
|
|
+ */
|
|
|
|
+ page_count = obj->base.size / PAGE_SIZE;
|
|
|
|
+ obj->pages = drm_malloc_ab(page_count, sizeof(struct page *));
|
|
|
|
+ if (obj->pages == NULL)
|
|
|
|
+ return -ENOMEM;
|
|
|
|
+
|
|
|
|
+ /* Fail silently without starting the shrinker */
|
|
|
|
+ mapping = obj->base.filp->f_path.dentry->d_inode->i_mapping;
|
|
|
|
+ gfp = mapping_gfp_mask(mapping);
|
|
|
|
+ gfp |= __GFP_NORETRY | __GFP_NOWARN;
|
|
|
|
+ gfp &= ~(__GFP_IO | __GFP_WAIT);
|
|
|
|
+ for (i = 0; i < page_count; i++) {
|
|
|
|
+ page = shmem_read_mapping_page_gfp(mapping, i, gfp);
|
|
|
|
+ if (IS_ERR(page)) {
|
|
|
|
+ i915_gem_purge(dev_priv, page_count);
|
|
|
|
+ page = shmem_read_mapping_page_gfp(mapping, i, gfp);
|
|
|
|
+ }
|
|
|
|
+ if (IS_ERR(page)) {
|
|
|
|
+ /* We've tried hard to allocate the memory by reaping
|
|
|
|
+ * our own buffer, now let the real VM do its job and
|
|
|
|
+ * go down in flames if truly OOM.
|
|
|
|
+ */
|
|
|
|
+ gfp &= ~(__GFP_NORETRY | __GFP_NOWARN);
|
|
|
|
+ gfp |= __GFP_IO | __GFP_WAIT;
|
|
|
|
+
|
|
|
|
+ i915_gem_shrink_all(dev_priv);
|
|
|
|
+ page = shmem_read_mapping_page_gfp(mapping, i, gfp);
|
|
|
|
+ if (IS_ERR(page))
|
|
|
|
+ goto err_pages;
|
|
|
|
+
|
|
|
|
+ gfp |= __GFP_NORETRY | __GFP_NOWARN;
|
|
|
|
+ gfp &= ~(__GFP_IO | __GFP_WAIT);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ obj->pages[i] = page;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (i915_gem_object_needs_bit17_swizzle(obj))
|
|
|
|
+ i915_gem_object_do_bit_17_swizzle(obj);
|
|
|
|
+
|
|
|
|
+ list_add_tail(&obj->gtt_list, &dev_priv->mm.unbound_list);
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+err_pages:
|
|
|
|
+ while (i--)
|
|
|
|
+ page_cache_release(obj->pages[i]);
|
|
|
|
+
|
|
|
|
+ drm_free_large(obj->pages);
|
|
|
|
+ obj->pages = NULL;
|
|
|
|
+ return PTR_ERR(page);
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
void
|
|
@@ -1486,32 +1885,6 @@ i915_gem_object_move_to_inactive(struct drm_i915_gem_object *obj)
|
|
WARN_ON(i915_verify_lists(dev));
|
|
WARN_ON(i915_verify_lists(dev));
|
|
}
|
|
}
|
|
|
|
|
|
-/* Immediately discard the backing storage */
|
|
|
|
-static void
|
|
|
|
-i915_gem_object_truncate(struct drm_i915_gem_object *obj)
|
|
|
|
-{
|
|
|
|
- struct inode *inode;
|
|
|
|
-
|
|
|
|
- /* Our goal here is to return as much of the memory as
|
|
|
|
- * is possible back to the system as we are called from OOM.
|
|
|
|
- * To do this we must instruct the shmfs to drop all of its
|
|
|
|
- * backing pages, *now*.
|
|
|
|
- */
|
|
|
|
- inode = obj->base.filp->f_path.dentry->d_inode;
|
|
|
|
- shmem_truncate_range(inode, 0, (loff_t)-1);
|
|
|
|
-
|
|
|
|
- if (obj->base.map_list.map)
|
|
|
|
- drm_gem_free_mmap_offset(&obj->base);
|
|
|
|
-
|
|
|
|
- obj->madv = __I915_MADV_PURGED;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-static inline int
|
|
|
|
-i915_gem_object_is_purgeable(struct drm_i915_gem_object *obj)
|
|
|
|
-{
|
|
|
|
- return obj->madv == I915_MADV_DONTNEED;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
static u32
|
|
static u32
|
|
i915_gem_get_seqno(struct drm_device *dev)
|
|
i915_gem_get_seqno(struct drm_device *dev)
|
|
{
|
|
{
|
|
@@ -1698,6 +2071,7 @@ void i915_gem_reset(struct drm_device *dev)
|
|
obj->base.read_domains &= ~I915_GEM_GPU_DOMAINS;
|
|
obj->base.read_domains &= ~I915_GEM_GPU_DOMAINS;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+
|
|
/* The fence registers are invalidated so clear them out */
|
|
/* The fence registers are invalidated so clear them out */
|
|
i915_gem_reset_fences(dev);
|
|
i915_gem_reset_fences(dev);
|
|
}
|
|
}
|
|
@@ -1714,302 +2088,111 @@ i915_gem_retire_requests_ring(struct intel_ring_buffer *ring)
|
|
if (list_empty(&ring->request_list))
|
|
if (list_empty(&ring->request_list))
|
|
return;
|
|
return;
|
|
|
|
|
|
- WARN_ON(i915_verify_lists(ring->dev));
|
|
|
|
-
|
|
|
|
- seqno = ring->get_seqno(ring, true);
|
|
|
|
-
|
|
|
|
- for (i = 0; i < ARRAY_SIZE(ring->sync_seqno); i++)
|
|
|
|
- if (seqno >= ring->sync_seqno[i])
|
|
|
|
- ring->sync_seqno[i] = 0;
|
|
|
|
-
|
|
|
|
- while (!list_empty(&ring->request_list)) {
|
|
|
|
- struct drm_i915_gem_request *request;
|
|
|
|
-
|
|
|
|
- request = list_first_entry(&ring->request_list,
|
|
|
|
- struct drm_i915_gem_request,
|
|
|
|
- list);
|
|
|
|
-
|
|
|
|
- if (!i915_seqno_passed(seqno, request->seqno))
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- trace_i915_gem_request_retire(ring, request->seqno);
|
|
|
|
- /* We know the GPU must have read the request to have
|
|
|
|
- * sent us the seqno + interrupt, so use the position
|
|
|
|
- * of tail of the request to update the last known position
|
|
|
|
- * of the GPU head.
|
|
|
|
- */
|
|
|
|
- ring->last_retired_head = request->tail;
|
|
|
|
-
|
|
|
|
- list_del(&request->list);
|
|
|
|
- i915_gem_request_remove_from_client(request);
|
|
|
|
- kfree(request);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* Move any buffers on the active list that are no longer referenced
|
|
|
|
- * by the ringbuffer to the flushing/inactive lists as appropriate.
|
|
|
|
- */
|
|
|
|
- while (!list_empty(&ring->active_list)) {
|
|
|
|
- struct drm_i915_gem_object *obj;
|
|
|
|
-
|
|
|
|
- obj = list_first_entry(&ring->active_list,
|
|
|
|
- struct drm_i915_gem_object,
|
|
|
|
- ring_list);
|
|
|
|
-
|
|
|
|
- if (!i915_seqno_passed(seqno, obj->last_read_seqno))
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- i915_gem_object_move_to_inactive(obj);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (unlikely(ring->trace_irq_seqno &&
|
|
|
|
- i915_seqno_passed(seqno, ring->trace_irq_seqno))) {
|
|
|
|
- ring->irq_put(ring);
|
|
|
|
- ring->trace_irq_seqno = 0;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- WARN_ON(i915_verify_lists(ring->dev));
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-void
|
|
|
|
-i915_gem_retire_requests(struct drm_device *dev)
|
|
|
|
-{
|
|
|
|
- drm_i915_private_t *dev_priv = dev->dev_private;
|
|
|
|
- struct intel_ring_buffer *ring;
|
|
|
|
- int i;
|
|
|
|
-
|
|
|
|
- for_each_ring(ring, dev_priv, i)
|
|
|
|
- i915_gem_retire_requests_ring(ring);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-static void
|
|
|
|
-i915_gem_retire_work_handler(struct work_struct *work)
|
|
|
|
-{
|
|
|
|
- drm_i915_private_t *dev_priv;
|
|
|
|
- struct drm_device *dev;
|
|
|
|
- struct intel_ring_buffer *ring;
|
|
|
|
- bool idle;
|
|
|
|
- int i;
|
|
|
|
-
|
|
|
|
- dev_priv = container_of(work, drm_i915_private_t,
|
|
|
|
- mm.retire_work.work);
|
|
|
|
- dev = dev_priv->dev;
|
|
|
|
-
|
|
|
|
- /* Come back later if the device is busy... */
|
|
|
|
- if (!mutex_trylock(&dev->struct_mutex)) {
|
|
|
|
- queue_delayed_work(dev_priv->wq, &dev_priv->mm.retire_work, HZ);
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- i915_gem_retire_requests(dev);
|
|
|
|
-
|
|
|
|
- /* Send a periodic flush down the ring so we don't hold onto GEM
|
|
|
|
- * objects indefinitely.
|
|
|
|
- */
|
|
|
|
- idle = true;
|
|
|
|
- for_each_ring(ring, dev_priv, i) {
|
|
|
|
- if (ring->gpu_caches_dirty)
|
|
|
|
- i915_add_request(ring, NULL, NULL);
|
|
|
|
-
|
|
|
|
- idle &= list_empty(&ring->request_list);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (!dev_priv->mm.suspended && !idle)
|
|
|
|
- queue_delayed_work(dev_priv->wq, &dev_priv->mm.retire_work, HZ);
|
|
|
|
- if (idle)
|
|
|
|
- intel_mark_idle(dev);
|
|
|
|
-
|
|
|
|
- mutex_unlock(&dev->struct_mutex);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-int
|
|
|
|
-i915_gem_check_wedge(struct drm_i915_private *dev_priv,
|
|
|
|
- bool interruptible)
|
|
|
|
-{
|
|
|
|
- if (atomic_read(&dev_priv->mm.wedged)) {
|
|
|
|
- struct completion *x = &dev_priv->error_completion;
|
|
|
|
- bool recovery_complete;
|
|
|
|
- unsigned long flags;
|
|
|
|
-
|
|
|
|
- /* Give the error handler a chance to run. */
|
|
|
|
- spin_lock_irqsave(&x->wait.lock, flags);
|
|
|
|
- recovery_complete = x->done > 0;
|
|
|
|
- spin_unlock_irqrestore(&x->wait.lock, flags);
|
|
|
|
-
|
|
|
|
- /* Non-interruptible callers can't handle -EAGAIN, hence return
|
|
|
|
- * -EIO unconditionally for these. */
|
|
|
|
- if (!interruptible)
|
|
|
|
- return -EIO;
|
|
|
|
-
|
|
|
|
- /* Recovery complete, but still wedged means reset failure. */
|
|
|
|
- if (recovery_complete)
|
|
|
|
- return -EIO;
|
|
|
|
-
|
|
|
|
- return -EAGAIN;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return 0;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/*
|
|
|
|
- * Compare seqno against outstanding lazy request. Emit a request if they are
|
|
|
|
- * equal.
|
|
|
|
- */
|
|
|
|
-static int
|
|
|
|
-i915_gem_check_olr(struct intel_ring_buffer *ring, u32 seqno)
|
|
|
|
-{
|
|
|
|
- int ret;
|
|
|
|
-
|
|
|
|
- BUG_ON(!mutex_is_locked(&ring->dev->struct_mutex));
|
|
|
|
-
|
|
|
|
- ret = 0;
|
|
|
|
- if (seqno == ring->outstanding_lazy_request)
|
|
|
|
- ret = i915_add_request(ring, NULL, NULL);
|
|
|
|
-
|
|
|
|
- return ret;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/**
|
|
|
|
- * __wait_seqno - wait until execution of seqno has finished
|
|
|
|
- * @ring: the ring expected to report seqno
|
|
|
|
- * @seqno: duh!
|
|
|
|
- * @interruptible: do an interruptible wait (normally yes)
|
|
|
|
- * @timeout: in - how long to wait (NULL forever); out - how much time remaining
|
|
|
|
- *
|
|
|
|
- * Returns 0 if the seqno was found within the alloted time. Else returns the
|
|
|
|
- * errno with remaining time filled in timeout argument.
|
|
|
|
- */
|
|
|
|
-static int __wait_seqno(struct intel_ring_buffer *ring, u32 seqno,
|
|
|
|
- bool interruptible, struct timespec *timeout)
|
|
|
|
-{
|
|
|
|
- drm_i915_private_t *dev_priv = ring->dev->dev_private;
|
|
|
|
- struct timespec before, now, wait_time={1,0};
|
|
|
|
- unsigned long timeout_jiffies;
|
|
|
|
- long end;
|
|
|
|
- bool wait_forever = true;
|
|
|
|
- int ret;
|
|
|
|
-
|
|
|
|
- if (i915_seqno_passed(ring->get_seqno(ring, true), seqno))
|
|
|
|
- return 0;
|
|
|
|
-
|
|
|
|
- trace_i915_gem_request_wait_begin(ring, seqno);
|
|
|
|
-
|
|
|
|
- if (timeout != NULL) {
|
|
|
|
- wait_time = *timeout;
|
|
|
|
- wait_forever = false;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- timeout_jiffies = timespec_to_jiffies(&wait_time);
|
|
|
|
|
|
+ WARN_ON(i915_verify_lists(ring->dev));
|
|
|
|
|
|
- if (WARN_ON(!ring->irq_get(ring)))
|
|
|
|
- return -ENODEV;
|
|
|
|
|
|
+ seqno = ring->get_seqno(ring, true);
|
|
|
|
|
|
- /* Record current time in case interrupted by signal, or wedged * */
|
|
|
|
- getrawmonotonic(&before);
|
|
|
|
|
|
+ for (i = 0; i < ARRAY_SIZE(ring->sync_seqno); i++)
|
|
|
|
+ if (seqno >= ring->sync_seqno[i])
|
|
|
|
+ ring->sync_seqno[i] = 0;
|
|
|
|
|
|
-#define EXIT_COND \
|
|
|
|
- (i915_seqno_passed(ring->get_seqno(ring, false), seqno) || \
|
|
|
|
- atomic_read(&dev_priv->mm.wedged))
|
|
|
|
- do {
|
|
|
|
- if (interruptible)
|
|
|
|
- end = wait_event_interruptible_timeout(ring->irq_queue,
|
|
|
|
- EXIT_COND,
|
|
|
|
- timeout_jiffies);
|
|
|
|
- else
|
|
|
|
- end = wait_event_timeout(ring->irq_queue, EXIT_COND,
|
|
|
|
- timeout_jiffies);
|
|
|
|
|
|
+ while (!list_empty(&ring->request_list)) {
|
|
|
|
+ struct drm_i915_gem_request *request;
|
|
|
|
|
|
- ret = i915_gem_check_wedge(dev_priv, interruptible);
|
|
|
|
- if (ret)
|
|
|
|
- end = ret;
|
|
|
|
- } while (end == 0 && wait_forever);
|
|
|
|
|
|
+ request = list_first_entry(&ring->request_list,
|
|
|
|
+ struct drm_i915_gem_request,
|
|
|
|
+ list);
|
|
|
|
|
|
- getrawmonotonic(&now);
|
|
|
|
|
|
+ if (!i915_seqno_passed(seqno, request->seqno))
|
|
|
|
+ break;
|
|
|
|
|
|
- ring->irq_put(ring);
|
|
|
|
- trace_i915_gem_request_wait_end(ring, seqno);
|
|
|
|
-#undef EXIT_COND
|
|
|
|
|
|
+ trace_i915_gem_request_retire(ring, request->seqno);
|
|
|
|
+ /* We know the GPU must have read the request to have
|
|
|
|
+ * sent us the seqno + interrupt, so use the position
|
|
|
|
+ * of tail of the request to update the last known position
|
|
|
|
+ * of the GPU head.
|
|
|
|
+ */
|
|
|
|
+ ring->last_retired_head = request->tail;
|
|
|
|
|
|
- if (timeout) {
|
|
|
|
- struct timespec sleep_time = timespec_sub(now, before);
|
|
|
|
- *timeout = timespec_sub(*timeout, sleep_time);
|
|
|
|
|
|
+ list_del(&request->list);
|
|
|
|
+ i915_gem_request_remove_from_client(request);
|
|
|
|
+ kfree(request);
|
|
}
|
|
}
|
|
|
|
|
|
- switch (end) {
|
|
|
|
- case -EIO:
|
|
|
|
- case -EAGAIN: /* Wedged */
|
|
|
|
- case -ERESTARTSYS: /* Signal */
|
|
|
|
- return (int)end;
|
|
|
|
- case 0: /* Timeout */
|
|
|
|
- if (timeout)
|
|
|
|
- set_normalized_timespec(timeout, 0, 0);
|
|
|
|
- return -ETIME;
|
|
|
|
- default: /* Completed */
|
|
|
|
- WARN_ON(end < 0); /* We're not aware of other errors */
|
|
|
|
- return 0;
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
|
|
+ /* Move any buffers on the active list that are no longer referenced
|
|
|
|
+ * by the ringbuffer to the flushing/inactive lists as appropriate.
|
|
|
|
+ */
|
|
|
|
+ while (!list_empty(&ring->active_list)) {
|
|
|
|
+ struct drm_i915_gem_object *obj;
|
|
|
|
|
|
-/**
|
|
|
|
- * Waits for a sequence number to be signaled, and cleans up the
|
|
|
|
- * request and object lists appropriately for that event.
|
|
|
|
- */
|
|
|
|
-int
|
|
|
|
-i915_wait_seqno(struct intel_ring_buffer *ring, uint32_t seqno)
|
|
|
|
-{
|
|
|
|
- drm_i915_private_t *dev_priv = ring->dev->dev_private;
|
|
|
|
- int ret = 0;
|
|
|
|
|
|
+ obj = list_first_entry(&ring->active_list,
|
|
|
|
+ struct drm_i915_gem_object,
|
|
|
|
+ ring_list);
|
|
|
|
|
|
- BUG_ON(seqno == 0);
|
|
|
|
|
|
+ if (!i915_seqno_passed(seqno, obj->last_read_seqno))
|
|
|
|
+ break;
|
|
|
|
|
|
- ret = i915_gem_check_wedge(dev_priv, dev_priv->mm.interruptible);
|
|
|
|
- if (ret)
|
|
|
|
- return ret;
|
|
|
|
|
|
+ i915_gem_object_move_to_inactive(obj);
|
|
|
|
+ }
|
|
|
|
|
|
- ret = i915_gem_check_olr(ring, seqno);
|
|
|
|
- if (ret)
|
|
|
|
- return ret;
|
|
|
|
|
|
+ if (unlikely(ring->trace_irq_seqno &&
|
|
|
|
+ i915_seqno_passed(seqno, ring->trace_irq_seqno))) {
|
|
|
|
+ ring->irq_put(ring);
|
|
|
|
+ ring->trace_irq_seqno = 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ WARN_ON(i915_verify_lists(ring->dev));
|
|
|
|
+}
|
|
|
|
|
|
- ret = __wait_seqno(ring, seqno, dev_priv->mm.interruptible, NULL);
|
|
|
|
|
|
+void
|
|
|
|
+i915_gem_retire_requests(struct drm_device *dev)
|
|
|
|
+{
|
|
|
|
+ drm_i915_private_t *dev_priv = dev->dev_private;
|
|
|
|
+ struct intel_ring_buffer *ring;
|
|
|
|
+ int i;
|
|
|
|
|
|
- return ret;
|
|
|
|
|
|
+ for_each_ring(ring, dev_priv, i)
|
|
|
|
+ i915_gem_retire_requests_ring(ring);
|
|
}
|
|
}
|
|
|
|
|
|
-/**
|
|
|
|
- * Ensures that all rendering to the object has completed and the object is
|
|
|
|
- * safe to unbind from the GTT or access from the CPU.
|
|
|
|
- */
|
|
|
|
-static __must_check int
|
|
|
|
-i915_gem_object_wait_rendering(struct drm_i915_gem_object *obj,
|
|
|
|
- bool readonly)
|
|
|
|
|
|
+static void
|
|
|
|
+i915_gem_retire_work_handler(struct work_struct *work)
|
|
{
|
|
{
|
|
- u32 seqno;
|
|
|
|
- int ret;
|
|
|
|
|
|
+ drm_i915_private_t *dev_priv;
|
|
|
|
+ struct drm_device *dev;
|
|
|
|
+ struct intel_ring_buffer *ring;
|
|
|
|
+ bool idle;
|
|
|
|
+ int i;
|
|
|
|
|
|
- /* If there is rendering queued on the buffer being evicted, wait for
|
|
|
|
- * it.
|
|
|
|
- */
|
|
|
|
- if (readonly)
|
|
|
|
- seqno = obj->last_write_seqno;
|
|
|
|
- else
|
|
|
|
- seqno = obj->last_read_seqno;
|
|
|
|
- if (seqno == 0)
|
|
|
|
- return 0;
|
|
|
|
|
|
+ dev_priv = container_of(work, drm_i915_private_t,
|
|
|
|
+ mm.retire_work.work);
|
|
|
|
+ dev = dev_priv->dev;
|
|
|
|
|
|
- ret = i915_wait_seqno(obj->ring, seqno);
|
|
|
|
- if (ret)
|
|
|
|
- return ret;
|
|
|
|
|
|
+ /* Come back later if the device is busy... */
|
|
|
|
+ if (!mutex_trylock(&dev->struct_mutex)) {
|
|
|
|
+ queue_delayed_work(dev_priv->wq, &dev_priv->mm.retire_work, HZ);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ i915_gem_retire_requests(dev);
|
|
|
|
|
|
- /* Manually manage the write flush as we may have not yet retired
|
|
|
|
- * the buffer.
|
|
|
|
|
|
+ /* Send a periodic flush down the ring so we don't hold onto GEM
|
|
|
|
+ * objects indefinitely.
|
|
*/
|
|
*/
|
|
- if (obj->last_write_seqno &&
|
|
|
|
- i915_seqno_passed(seqno, obj->last_write_seqno)) {
|
|
|
|
- obj->last_write_seqno = 0;
|
|
|
|
- obj->base.write_domain &= ~I915_GEM_GPU_DOMAINS;
|
|
|
|
|
|
+ idle = true;
|
|
|
|
+ for_each_ring(ring, dev_priv, i) {
|
|
|
|
+ if (ring->gpu_caches_dirty)
|
|
|
|
+ i915_add_request(ring, NULL, NULL);
|
|
|
|
+
|
|
|
|
+ idle &= list_empty(&ring->request_list);
|
|
}
|
|
}
|
|
|
|
|
|
- i915_gem_retire_requests_ring(obj->ring);
|
|
|
|
- return 0;
|
|
|
|
|
|
+ if (!dev_priv->mm.suspended && !idle)
|
|
|
|
+ queue_delayed_work(dev_priv->wq, &dev_priv->mm.retire_work, HZ);
|
|
|
|
+ if (idle)
|
|
|
|
+ intel_mark_idle(dev);
|
|
|
|
+
|
|
|
|
+ mutex_unlock(&dev->struct_mutex);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -2199,6 +2382,8 @@ i915_gem_object_unbind(struct drm_i915_gem_object *obj)
|
|
if (obj->pin_count)
|
|
if (obj->pin_count)
|
|
return -EBUSY;
|
|
return -EBUSY;
|
|
|
|
|
|
|
|
+ BUG_ON(obj->pages == NULL);
|
|
|
|
+
|
|
ret = i915_gem_object_finish_gpu(obj);
|
|
ret = i915_gem_object_finish_gpu(obj);
|
|
if (ret)
|
|
if (ret)
|
|
return ret;
|
|
return ret;
|
|
@@ -2209,22 +2394,6 @@ i915_gem_object_unbind(struct drm_i915_gem_object *obj)
|
|
|
|
|
|
i915_gem_object_finish_gtt(obj);
|
|
i915_gem_object_finish_gtt(obj);
|
|
|
|
|
|
- /* Move the object to the CPU domain to ensure that
|
|
|
|
- * any possible CPU writes while it's not in the GTT
|
|
|
|
- * are flushed when we go to remap it.
|
|
|
|
- */
|
|
|
|
- if (ret == 0)
|
|
|
|
- ret = i915_gem_object_set_to_cpu_domain(obj, 1);
|
|
|
|
- if (ret == -ERESTARTSYS)
|
|
|
|
- return ret;
|
|
|
|
- if (ret) {
|
|
|
|
- /* In the event of a disaster, abandon all caches and
|
|
|
|
- * hope for the best.
|
|
|
|
- */
|
|
|
|
- i915_gem_clflush_object(obj);
|
|
|
|
- obj->base.read_domains = obj->base.write_domain = I915_GEM_DOMAIN_CPU;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
/* release the fence reg _after_ flushing */
|
|
/* release the fence reg _after_ flushing */
|
|
ret = i915_gem_object_put_fence(obj);
|
|
ret = i915_gem_object_put_fence(obj);
|
|
if (ret)
|
|
if (ret)
|
|
@@ -2240,10 +2409,8 @@ i915_gem_object_unbind(struct drm_i915_gem_object *obj)
|
|
}
|
|
}
|
|
i915_gem_gtt_finish_object(obj);
|
|
i915_gem_gtt_finish_object(obj);
|
|
|
|
|
|
- i915_gem_object_put_pages_gtt(obj);
|
|
|
|
-
|
|
|
|
- list_del_init(&obj->gtt_list);
|
|
|
|
- list_del_init(&obj->mm_list);
|
|
|
|
|
|
+ list_del(&obj->mm_list);
|
|
|
|
+ list_move_tail(&obj->gtt_list, &dev_priv->mm.unbound_list);
|
|
/* Avoid an unnecessary call to unbind on rebind. */
|
|
/* Avoid an unnecessary call to unbind on rebind. */
|
|
obj->map_and_fenceable = true;
|
|
obj->map_and_fenceable = true;
|
|
|
|
|
|
@@ -2251,10 +2418,7 @@ i915_gem_object_unbind(struct drm_i915_gem_object *obj)
|
|
obj->gtt_space = NULL;
|
|
obj->gtt_space = NULL;
|
|
obj->gtt_offset = 0;
|
|
obj->gtt_offset = 0;
|
|
|
|
|
|
- if (i915_gem_object_is_purgeable(obj))
|
|
|
|
- i915_gem_object_truncate(obj);
|
|
|
|
-
|
|
|
|
- return ret;
|
|
|
|
|
|
+ return 0;
|
|
}
|
|
}
|
|
|
|
|
|
static int i915_ring_idle(struct intel_ring_buffer *ring)
|
|
static int i915_ring_idle(struct intel_ring_buffer *ring)
|
|
@@ -2662,12 +2826,12 @@ static void i915_gem_verify_gtt(struct drm_device *dev)
|
|
static int
|
|
static int
|
|
i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj,
|
|
i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj,
|
|
unsigned alignment,
|
|
unsigned alignment,
|
|
- bool map_and_fenceable)
|
|
|
|
|
|
+ bool map_and_fenceable,
|
|
|
|
+ bool nonblocking)
|
|
{
|
|
{
|
|
struct drm_device *dev = obj->base.dev;
|
|
struct drm_device *dev = obj->base.dev;
|
|
drm_i915_private_t *dev_priv = dev->dev_private;
|
|
drm_i915_private_t *dev_priv = dev->dev_private;
|
|
struct drm_mm_node *free_space;
|
|
struct drm_mm_node *free_space;
|
|
- gfp_t gfpmask = __GFP_NORETRY | __GFP_NOWARN;
|
|
|
|
u32 size, fence_size, fence_alignment, unfenced_alignment;
|
|
u32 size, fence_size, fence_alignment, unfenced_alignment;
|
|
bool mappable, fenceable;
|
|
bool mappable, fenceable;
|
|
int ret;
|
|
int ret;
|
|
@@ -2707,6 +2871,10 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj,
|
|
return -E2BIG;
|
|
return -E2BIG;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ ret = i915_gem_object_get_pages_gtt(obj);
|
|
|
|
+ if (ret)
|
|
|
|
+ return ret;
|
|
|
|
+
|
|
search_free:
|
|
search_free:
|
|
if (map_and_fenceable)
|
|
if (map_and_fenceable)
|
|
free_space =
|
|
free_space =
|
|
@@ -2733,12 +2901,10 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj,
|
|
false);
|
|
false);
|
|
}
|
|
}
|
|
if (obj->gtt_space == NULL) {
|
|
if (obj->gtt_space == NULL) {
|
|
- /* If the gtt is empty and we're still having trouble
|
|
|
|
- * fitting our object in, we're out of memory.
|
|
|
|
- */
|
|
|
|
ret = i915_gem_evict_something(dev, size, alignment,
|
|
ret = i915_gem_evict_something(dev, size, alignment,
|
|
obj->cache_level,
|
|
obj->cache_level,
|
|
- map_and_fenceable);
|
|
|
|
|
|
+ map_and_fenceable,
|
|
|
|
+ nonblocking);
|
|
if (ret)
|
|
if (ret)
|
|
return ret;
|
|
return ret;
|
|
|
|
|
|
@@ -2752,55 +2918,20 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj,
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
|
|
- ret = i915_gem_object_get_pages_gtt(obj, gfpmask);
|
|
|
|
- if (ret) {
|
|
|
|
- drm_mm_put_block(obj->gtt_space);
|
|
|
|
- obj->gtt_space = NULL;
|
|
|
|
-
|
|
|
|
- if (ret == -ENOMEM) {
|
|
|
|
- /* first try to reclaim some memory by clearing the GTT */
|
|
|
|
- ret = i915_gem_evict_everything(dev, false);
|
|
|
|
- if (ret) {
|
|
|
|
- /* now try to shrink everyone else */
|
|
|
|
- if (gfpmask) {
|
|
|
|
- gfpmask = 0;
|
|
|
|
- goto search_free;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return -ENOMEM;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- goto search_free;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return ret;
|
|
|
|
- }
|
|
|
|
|
|
|
|
ret = i915_gem_gtt_prepare_object(obj);
|
|
ret = i915_gem_gtt_prepare_object(obj);
|
|
if (ret) {
|
|
if (ret) {
|
|
- i915_gem_object_put_pages_gtt(obj);
|
|
|
|
drm_mm_put_block(obj->gtt_space);
|
|
drm_mm_put_block(obj->gtt_space);
|
|
obj->gtt_space = NULL;
|
|
obj->gtt_space = NULL;
|
|
-
|
|
|
|
- if (i915_gem_evict_everything(dev, false))
|
|
|
|
- return ret;
|
|
|
|
-
|
|
|
|
- goto search_free;
|
|
|
|
|
|
+ return ret;
|
|
}
|
|
}
|
|
|
|
|
|
if (!dev_priv->mm.aliasing_ppgtt)
|
|
if (!dev_priv->mm.aliasing_ppgtt)
|
|
i915_gem_gtt_bind_object(obj, obj->cache_level);
|
|
i915_gem_gtt_bind_object(obj, obj->cache_level);
|
|
|
|
|
|
- list_add_tail(&obj->gtt_list, &dev_priv->mm.gtt_list);
|
|
|
|
|
|
+ list_move_tail(&obj->gtt_list, &dev_priv->mm.bound_list);
|
|
list_add_tail(&obj->mm_list, &dev_priv->mm.inactive_list);
|
|
list_add_tail(&obj->mm_list, &dev_priv->mm.inactive_list);
|
|
|
|
|
|
- /* Assert that the object is not currently in any GPU domain. As it
|
|
|
|
- * wasn't in the GTT, there shouldn't be any way it could have been in
|
|
|
|
- * a GPU cache
|
|
|
|
- */
|
|
|
|
- BUG_ON(obj->base.read_domains & I915_GEM_GPU_DOMAINS);
|
|
|
|
- BUG_ON(obj->base.write_domain & I915_GEM_GPU_DOMAINS);
|
|
|
|
-
|
|
|
|
obj->gtt_offset = obj->gtt_space->start;
|
|
obj->gtt_offset = obj->gtt_space->start;
|
|
|
|
|
|
fenceable =
|
|
fenceable =
|
|
@@ -3113,7 +3244,7 @@ i915_gem_object_pin_to_display_plane(struct drm_i915_gem_object *obj,
|
|
* (e.g. libkms for the bootup splash), we have to ensure that we
|
|
* (e.g. libkms for the bootup splash), we have to ensure that we
|
|
* always use map_and_fenceable for all scanout buffers.
|
|
* always use map_and_fenceable for all scanout buffers.
|
|
*/
|
|
*/
|
|
- ret = i915_gem_object_pin(obj, alignment, true);
|
|
|
|
|
|
+ ret = i915_gem_object_pin(obj, alignment, true, false);
|
|
if (ret)
|
|
if (ret)
|
|
return ret;
|
|
return ret;
|
|
|
|
|
|
@@ -3250,7 +3381,8 @@ i915_gem_ring_throttle(struct drm_device *dev, struct drm_file *file)
|
|
int
|
|
int
|
|
i915_gem_object_pin(struct drm_i915_gem_object *obj,
|
|
i915_gem_object_pin(struct drm_i915_gem_object *obj,
|
|
uint32_t alignment,
|
|
uint32_t alignment,
|
|
- bool map_and_fenceable)
|
|
|
|
|
|
+ bool map_and_fenceable,
|
|
|
|
+ bool nonblocking)
|
|
{
|
|
{
|
|
int ret;
|
|
int ret;
|
|
|
|
|
|
@@ -3274,7 +3406,8 @@ i915_gem_object_pin(struct drm_i915_gem_object *obj,
|
|
|
|
|
|
if (obj->gtt_space == NULL) {
|
|
if (obj->gtt_space == NULL) {
|
|
ret = i915_gem_object_bind_to_gtt(obj, alignment,
|
|
ret = i915_gem_object_bind_to_gtt(obj, alignment,
|
|
- map_and_fenceable);
|
|
|
|
|
|
+ map_and_fenceable,
|
|
|
|
+ nonblocking);
|
|
if (ret)
|
|
if (ret)
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
@@ -3332,7 +3465,7 @@ i915_gem_pin_ioctl(struct drm_device *dev, void *data,
|
|
obj->user_pin_count++;
|
|
obj->user_pin_count++;
|
|
obj->pin_filp = file;
|
|
obj->pin_filp = file;
|
|
if (obj->user_pin_count == 1) {
|
|
if (obj->user_pin_count == 1) {
|
|
- ret = i915_gem_object_pin(obj, args->alignment, true);
|
|
|
|
|
|
+ ret = i915_gem_object_pin(obj, args->alignment, true, false);
|
|
if (ret)
|
|
if (ret)
|
|
goto out;
|
|
goto out;
|
|
}
|
|
}
|
|
@@ -3464,9 +3597,8 @@ i915_gem_madvise_ioctl(struct drm_device *dev, void *data,
|
|
if (obj->madv != __I915_MADV_PURGED)
|
|
if (obj->madv != __I915_MADV_PURGED)
|
|
obj->madv = args->madv;
|
|
obj->madv = args->madv;
|
|
|
|
|
|
- /* if the object is no longer bound, discard its backing storage */
|
|
|
|
- if (i915_gem_object_is_purgeable(obj) &&
|
|
|
|
- obj->gtt_space == NULL)
|
|
|
|
|
|
+ /* if the object is no longer attached, discard its backing storage */
|
|
|
|
+ if (i915_gem_object_is_purgeable(obj) && obj->pages == NULL)
|
|
i915_gem_object_truncate(obj);
|
|
i915_gem_object_truncate(obj);
|
|
|
|
|
|
args->retained = obj->madv != __I915_MADV_PURGED;
|
|
args->retained = obj->madv != __I915_MADV_PURGED;
|
|
@@ -3478,10 +3610,26 @@ unlock:
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+void i915_gem_object_init(struct drm_i915_gem_object *obj)
|
|
|
|
+{
|
|
|
|
+ obj->base.driver_private = NULL;
|
|
|
|
+
|
|
|
|
+ INIT_LIST_HEAD(&obj->mm_list);
|
|
|
|
+ INIT_LIST_HEAD(&obj->gtt_list);
|
|
|
|
+ INIT_LIST_HEAD(&obj->ring_list);
|
|
|
|
+ INIT_LIST_HEAD(&obj->exec_list);
|
|
|
|
+
|
|
|
|
+ obj->fence_reg = I915_FENCE_REG_NONE;
|
|
|
|
+ obj->madv = I915_MADV_WILLNEED;
|
|
|
|
+ /* Avoid an unnecessary call to unbind on the first bind. */
|
|
|
|
+ obj->map_and_fenceable = true;
|
|
|
|
+
|
|
|
|
+ i915_gem_info_add_obj(obj->base.dev->dev_private, obj->base.size);
|
|
|
|
+}
|
|
|
|
+
|
|
struct drm_i915_gem_object *i915_gem_alloc_object(struct drm_device *dev,
|
|
struct drm_i915_gem_object *i915_gem_alloc_object(struct drm_device *dev,
|
|
size_t size)
|
|
size_t size)
|
|
{
|
|
{
|
|
- struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
|
struct drm_i915_gem_object *obj;
|
|
struct drm_i915_gem_object *obj;
|
|
struct address_space *mapping;
|
|
struct address_space *mapping;
|
|
u32 mask;
|
|
u32 mask;
|
|
@@ -3505,7 +3653,7 @@ struct drm_i915_gem_object *i915_gem_alloc_object(struct drm_device *dev,
|
|
mapping = obj->base.filp->f_path.dentry->d_inode->i_mapping;
|
|
mapping = obj->base.filp->f_path.dentry->d_inode->i_mapping;
|
|
mapping_set_gfp_mask(mapping, mask);
|
|
mapping_set_gfp_mask(mapping, mask);
|
|
|
|
|
|
- i915_gem_info_add_obj(dev_priv, size);
|
|
|
|
|
|
+ i915_gem_object_init(obj);
|
|
|
|
|
|
obj->base.write_domain = I915_GEM_DOMAIN_CPU;
|
|
obj->base.write_domain = I915_GEM_DOMAIN_CPU;
|
|
obj->base.read_domains = I915_GEM_DOMAIN_CPU;
|
|
obj->base.read_domains = I915_GEM_DOMAIN_CPU;
|
|
@@ -3527,16 +3675,6 @@ struct drm_i915_gem_object *i915_gem_alloc_object(struct drm_device *dev,
|
|
} else
|
|
} else
|
|
obj->cache_level = I915_CACHE_NONE;
|
|
obj->cache_level = I915_CACHE_NONE;
|
|
|
|
|
|
- obj->base.driver_private = NULL;
|
|
|
|
- obj->fence_reg = I915_FENCE_REG_NONE;
|
|
|
|
- INIT_LIST_HEAD(&obj->mm_list);
|
|
|
|
- INIT_LIST_HEAD(&obj->gtt_list);
|
|
|
|
- INIT_LIST_HEAD(&obj->ring_list);
|
|
|
|
- INIT_LIST_HEAD(&obj->exec_list);
|
|
|
|
- obj->madv = I915_MADV_WILLNEED;
|
|
|
|
- /* Avoid an unnecessary call to unbind on the first bind. */
|
|
|
|
- obj->map_and_fenceable = true;
|
|
|
|
-
|
|
|
|
return obj;
|
|
return obj;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -3573,8 +3711,8 @@ void i915_gem_free_object(struct drm_gem_object *gem_obj)
|
|
dev_priv->mm.interruptible = was_interruptible;
|
|
dev_priv->mm.interruptible = was_interruptible;
|
|
}
|
|
}
|
|
|
|
|
|
- if (obj->base.map_list.map)
|
|
|
|
- drm_gem_free_mmap_offset(&obj->base);
|
|
|
|
|
|
+ i915_gem_object_put_pages_gtt(obj);
|
|
|
|
+ i915_gem_object_free_mmap_offset(obj);
|
|
|
|
|
|
drm_gem_object_release(&obj->base);
|
|
drm_gem_object_release(&obj->base);
|
|
i915_gem_info_remove_obj(dev_priv, obj->base.size);
|
|
i915_gem_info_remove_obj(dev_priv, obj->base.size);
|
|
@@ -3605,7 +3743,7 @@ i915_gem_idle(struct drm_device *dev)
|
|
|
|
|
|
/* Under UMS, be paranoid and evict. */
|
|
/* Under UMS, be paranoid and evict. */
|
|
if (!drm_core_check_feature(dev, DRIVER_MODESET))
|
|
if (!drm_core_check_feature(dev, DRIVER_MODESET))
|
|
- i915_gem_evict_everything(dev, false);
|
|
|
|
|
|
+ i915_gem_evict_everything(dev);
|
|
|
|
|
|
i915_gem_reset_fences(dev);
|
|
i915_gem_reset_fences(dev);
|
|
|
|
|
|
@@ -3963,8 +4101,9 @@ i915_gem_load(struct drm_device *dev)
|
|
|
|
|
|
INIT_LIST_HEAD(&dev_priv->mm.active_list);
|
|
INIT_LIST_HEAD(&dev_priv->mm.active_list);
|
|
INIT_LIST_HEAD(&dev_priv->mm.inactive_list);
|
|
INIT_LIST_HEAD(&dev_priv->mm.inactive_list);
|
|
|
|
+ INIT_LIST_HEAD(&dev_priv->mm.unbound_list);
|
|
|
|
+ INIT_LIST_HEAD(&dev_priv->mm.bound_list);
|
|
INIT_LIST_HEAD(&dev_priv->mm.fence_list);
|
|
INIT_LIST_HEAD(&dev_priv->mm.fence_list);
|
|
- INIT_LIST_HEAD(&dev_priv->mm.gtt_list);
|
|
|
|
for (i = 0; i < I915_NUM_RINGS; i++)
|
|
for (i = 0; i < I915_NUM_RINGS; i++)
|
|
init_ring_lists(&dev_priv->ring[i]);
|
|
init_ring_lists(&dev_priv->ring[i]);
|
|
for (i = 0; i < I915_MAX_NUM_FENCES; i++)
|
|
for (i = 0; i < I915_MAX_NUM_FENCES; i++)
|
|
@@ -4208,13 +4347,6 @@ void i915_gem_release(struct drm_device *dev, struct drm_file *file)
|
|
spin_unlock(&file_priv->mm.lock);
|
|
spin_unlock(&file_priv->mm.lock);
|
|
}
|
|
}
|
|
|
|
|
|
-static int
|
|
|
|
-i915_gpu_is_active(struct drm_device *dev)
|
|
|
|
-{
|
|
|
|
- drm_i915_private_t *dev_priv = dev->dev_private;
|
|
|
|
- return !list_empty(&dev_priv->mm.active_list);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
static int
|
|
static int
|
|
i915_gem_inactive_shrink(struct shrinker *shrinker, struct shrink_control *sc)
|
|
i915_gem_inactive_shrink(struct shrinker *shrinker, struct shrink_control *sc)
|
|
{
|
|
{
|
|
@@ -4223,60 +4355,26 @@ i915_gem_inactive_shrink(struct shrinker *shrinker, struct shrink_control *sc)
|
|
struct drm_i915_private,
|
|
struct drm_i915_private,
|
|
mm.inactive_shrinker);
|
|
mm.inactive_shrinker);
|
|
struct drm_device *dev = dev_priv->dev;
|
|
struct drm_device *dev = dev_priv->dev;
|
|
- struct drm_i915_gem_object *obj, *next;
|
|
|
|
|
|
+ struct drm_i915_gem_object *obj;
|
|
int nr_to_scan = sc->nr_to_scan;
|
|
int nr_to_scan = sc->nr_to_scan;
|
|
int cnt;
|
|
int cnt;
|
|
|
|
|
|
if (!mutex_trylock(&dev->struct_mutex))
|
|
if (!mutex_trylock(&dev->struct_mutex))
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
- /* "fast-path" to count number of available objects */
|
|
|
|
- if (nr_to_scan == 0) {
|
|
|
|
- cnt = 0;
|
|
|
|
- list_for_each_entry(obj,
|
|
|
|
- &dev_priv->mm.inactive_list,
|
|
|
|
- mm_list)
|
|
|
|
- cnt++;
|
|
|
|
- mutex_unlock(&dev->struct_mutex);
|
|
|
|
- return cnt / 100 * sysctl_vfs_cache_pressure;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
-rescan:
|
|
|
|
- /* first scan for clean buffers */
|
|
|
|
- i915_gem_retire_requests(dev);
|
|
|
|
-
|
|
|
|
- list_for_each_entry_safe(obj, next,
|
|
|
|
- &dev_priv->mm.inactive_list,
|
|
|
|
- mm_list) {
|
|
|
|
- if (i915_gem_object_is_purgeable(obj)) {
|
|
|
|
- if (i915_gem_object_unbind(obj) == 0 &&
|
|
|
|
- --nr_to_scan == 0)
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
|
|
+ if (nr_to_scan) {
|
|
|
|
+ nr_to_scan -= i915_gem_purge(dev_priv, nr_to_scan);
|
|
|
|
+ if (nr_to_scan > 0)
|
|
|
|
+ i915_gem_shrink_all(dev_priv);
|
|
}
|
|
}
|
|
|
|
|
|
- /* second pass, evict/count anything still on the inactive list */
|
|
|
|
cnt = 0;
|
|
cnt = 0;
|
|
- list_for_each_entry_safe(obj, next,
|
|
|
|
- &dev_priv->mm.inactive_list,
|
|
|
|
- mm_list) {
|
|
|
|
- if (nr_to_scan &&
|
|
|
|
- i915_gem_object_unbind(obj) == 0)
|
|
|
|
- nr_to_scan--;
|
|
|
|
- else
|
|
|
|
- cnt++;
|
|
|
|
- }
|
|
|
|
|
|
+ list_for_each_entry(obj, &dev_priv->mm.unbound_list, gtt_list)
|
|
|
|
+ cnt += obj->base.size >> PAGE_SHIFT;
|
|
|
|
+ list_for_each_entry(obj, &dev_priv->mm.bound_list, gtt_list)
|
|
|
|
+ if (obj->pin_count == 0)
|
|
|
|
+ cnt += obj->base.size >> PAGE_SHIFT;
|
|
|
|
|
|
- if (nr_to_scan && i915_gpu_is_active(dev)) {
|
|
|
|
- /*
|
|
|
|
- * We are desperate for pages, so as a last resort, wait
|
|
|
|
- * for the GPU to finish and discard whatever we can.
|
|
|
|
- * This has a dramatic impact to reduce the number of
|
|
|
|
- * OOM-killer events whilst running the GPU aggressively.
|
|
|
|
- */
|
|
|
|
- if (i915_gpu_idle(dev) == 0)
|
|
|
|
- goto rescan;
|
|
|
|
- }
|
|
|
|
mutex_unlock(&dev->struct_mutex);
|
|
mutex_unlock(&dev->struct_mutex);
|
|
- return cnt / 100 * sysctl_vfs_cache_pressure;
|
|
|
|
|
|
+ return cnt;
|
|
}
|
|
}
|